Skip to content

Commit

Permalink
Extract ReplicaSet name from pod name (#1202)
Browse files Browse the repository at this point in the history
* Extract ReplicaSet name from pod name

* Fix k8s test

* Fix disabledinformers test
  • Loading branch information
mariomac authored Sep 26, 2024
1 parent 6960d77 commit 9d41b5c
Show file tree
Hide file tree
Showing 33 changed files with 116 additions and 85 deletions.
1 change: 1 addition & 0 deletions pkg/export/attributes/attr_defs.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func getDefinitions(groups AttrGroups) map[Section]AttrReportGroup {
attr.K8sPodUID: true,
attr.K8sPodStartTime: true,
attr.K8sClusterName: true,
attr.K8sOwnerName: true,
},
}

Expand Down
20 changes: 10 additions & 10 deletions pkg/export/attributes/names/attrs.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,16 @@ const (
MessagingSystem = Name(semconv.MessagingSystemKey)
MessagingDestination = Name(semconv.MessagingDestinationNameKey)

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")
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")
K8sOwnerName = 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/discover/watcher_kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/mariomac/pipes/pipe"
"k8s.io/client-go/tools/cache"

attr "github.com/grafana/beyla/pkg/export/attributes/names"
"github.com/grafana/beyla/pkg/internal/helpers/container"
"github.com/grafana/beyla/pkg/internal/helpers/maps"
"github.com/grafana/beyla/pkg/internal/imetrics"
Expand Down Expand Up @@ -338,20 +339,12 @@ func withMetadata(pp processAttrs, info *kube.PodInfo) processAttrs {
services.AttrPodName: info.Name,
}
ret.podLabels = info.Labels
owner := info.Owner
for owner != nil {
ret.metadata[services.AttrOwnerName] = owner.Name
switch owner.LabelName {
case kube.OwnerDaemonSet:
ret.metadata[services.AttrDaemonSetName] = owner.Name
case kube.OwnerReplicaSet:
ret.metadata[services.AttrReplicaSetName] = owner.Name
case kube.OwnerDeployment:
ret.metadata[services.AttrDeploymentName] = owner.Name
case kube.OwnerStatefulSet:
ret.metadata[services.AttrStatefulSetName] = owner.Name
}
owner = owner.Owner

if info.Owner != nil {
ret.metadata[attr.Name(info.Owner.LabelName).Prom()] = info.Owner.Name
topName, topLabel := info.Owner.TopOwnerNameLabel()
ret.metadata[attr.Name(topLabel).Prom()] = topName
ret.metadata[services.AttrOwnerName] = topName
}
return ret
}
12 changes: 6 additions & 6 deletions pkg/internal/discover/watcher_kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ const (
containerPID = 123
containerID = "container-123"
containerPort = 332
replicaSetName = "rs-3321"
replicaSetName = "the-deployment-123456789"
deploymentName = "the-deployment"
podName = "the-pod"
podName = "the-deployment-123456789-abcde"
)

func TestWatcherKubeEnricher(t *testing.T) {
Expand Down Expand Up @@ -155,8 +155,8 @@ func TestWatcherKubeEnricherWithMatcher(t *testing.T) {
newProcess(inputCh, 123, []uint32{777})
newProcess(inputCh, 456, []uint32{})
newProcess(inputCh, 789, []uint32{443})
deployOwnedPod(t, k8sClient, namespace, "pod-789", "rs-789", "container-789")
deployReplicaSet(t, k8sClient, namespace, "rs-789", "ouyeah")
deployOwnedPod(t, k8sClient, namespace, "depl-rsid-podid", "depl-rsid", "container-789")
deployReplicaSet(t, k8sClient, namespace, "depl-rsid", "depl")
})

// sending events that will match and will be forwarded
Expand Down Expand Up @@ -205,8 +205,8 @@ func TestWatcherKubeEnricherWithMatcher(t *testing.T) {

t.Run("both process and metadata match", func(t *testing.T) {
newProcess(inputCh, 56, []uint32{443})
deployOwnedPod(t, k8sClient, namespace, "pod-56", "rs-56", "container-56")
deployReplicaSet(t, k8sClient, namespace, "rs-56", "chacha")
deployOwnedPod(t, k8sClient, namespace, "chacha-rsid-podid", "chacha-rsid", "container-56")
deployReplicaSet(t, k8sClient, namespace, "chacha-rsid", "chacha")
matches := testutil.ReadChannel(t, outputCh, timeout)
require.Len(t, matches, 1)
m := matches[0]
Expand Down
2 changes: 1 addition & 1 deletion pkg/internal/ebpf/httpssl/bpf_x86_bpfel.o
Git LFS file not shown
10 changes: 2 additions & 8 deletions pkg/internal/kube/informer.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,8 @@ func (k *Metadata) AddNodeEventHandler(h cache.ResourceEventHandler) error {
}

func (i *PodInfo) ServiceName() string {
if i.Owner != nil {
// we have two levels of ownership at most
if i.Owner.Owner != nil {
return i.Owner.Owner.Name
}

return i.Owner.Name
if on, _ := i.Owner.TopOwnerNameLabel(); on != "" {
return on
}

return i.Name
}
17 changes: 2 additions & 15 deletions pkg/internal/kube/informer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,14 @@ func TestServiceName(t *testing.T) {

pod2 := PodInfo{
Owner: &Owner{
LabelName: OwnerReplicaSet,
Owner: &Owner{
Name: "nested_two",
},
},
}

pod3 := PodInfo{
Owner: &Owner{
Owner: &Owner{
Owner: &Owner{
Name: "nested_three",
},
},
},
ObjectMeta: metav1.ObjectMeta{
Name: "too_nested",
},
}

pod4 := PodInfo{
ObjectMeta: metav1.ObjectMeta{
Name: "not_nested",
},
Expand All @@ -45,7 +33,6 @@ func TestServiceName(t *testing.T) {

assert.Equal(t, "nested_one", pod.ServiceName())
assert.Equal(t, "nested_two", pod2.ServiceName())
assert.Equal(t, "", pod3.ServiceName())
assert.Equal(t, "not_nested", pod4.ServiceName())
assert.Equal(t, "not_nested", pod3.ServiceName())
assert.Equal(t, "", pod5.ServiceName())
}
30 changes: 28 additions & 2 deletions pkg/internal/kube/owner.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const (
OwnerDeployment = OwnerLabel(attr.K8sDeploymentName)
OwnerStatefulSet = OwnerLabel(attr.K8sStatefulSetName)
OwnerDaemonSet = OwnerLabel(attr.K8sDaemonSetName)
OwnerUnknown = OwnerLabel(attr.K8sUnknownOwnerName)
OwnerGeneric = OwnerLabel(attr.K8sOwnerName)
)

type Owner struct {
Expand Down Expand Up @@ -57,11 +57,37 @@ func OwnerFrom(orefs []metav1.OwnerReference) *Owner {

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

// TopOwnerNameLabel returns the top-level name and metadata label in the owner chain.
// For example, if the owner is a ReplicaSet, it will return the Deployment name.
func (o *Owner) TopOwnerNameLabel() (string, OwnerLabel) {
if o == nil {
return "", ""
}
if o.LabelName == OwnerReplicaSet {
// we have two levels of ownership at most
if o.Owner != nil {
return o.Owner.Name, o.Owner.LabelName
}
// if the replicaset informer is disabled, we can't get the owner deployment,
// so we will heuristically extract it from the ReplicaSet Name (and cache it)
topOwnerName := o.Name
if idx := strings.LastIndexByte(topOwnerName, '-'); idx > 0 {
topOwnerName = topOwnerName[:idx]
o.Owner = &Owner{
Name: topOwnerName,
LabelName: OwnerDeployment,
}
return topOwnerName, OwnerDeployment
}
}
return o.Name, o.LabelName
}

func (o *Owner) String() string {
sb := strings.Builder{}
o.string(&sb)
Expand Down
33 changes: 31 additions & 2 deletions pkg/internal/kube/owner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestOwnerFrom_Unrecognized(t *testing.T) {
})
require.NotNil(t, owner)
assert.Equal(t, &Owner{
LabelName: OwnerUnknown,
LabelName: OwnerGeneric,
Name: "theowner",
}, owner)
}
Expand All @@ -52,7 +52,36 @@ func TestOwnerFrom_Unrecognized_AppsV1(t *testing.T) {
})
require.NotNil(t, owner)
assert.Equal(t, &Owner{
LabelName: OwnerUnknown,
LabelName: OwnerGeneric,
Name: "theowner",
}, owner)
}

func TestTopOwnerLabel(t *testing.T) {
type testCase struct {
expectedLabel OwnerLabel
expectedName string
owner *Owner
}
for _, tc := range []testCase{
{owner: nil},
{expectedLabel: OwnerDaemonSet, expectedName: "ds",
owner: &Owner{LabelName: OwnerDaemonSet, Name: "ds"}},
{expectedLabel: OwnerDeployment, expectedName: "rs-without-dep-meta",
owner: &Owner{LabelName: OwnerReplicaSet, Name: "rs-without-dep-meta-34fb1fa3a"}},
{expectedLabel: OwnerDeployment, expectedName: "dep",
owner: &Owner{LabelName: OwnerReplicaSet, Name: "dep-34fb1fa3a",
Owner: &Owner{LabelName: OwnerDeployment, Name: "dep"}}},
} {
t.Run(tc.expectedName, func(t *testing.T) {
name, label := tc.owner.TopOwnerNameLabel()
assert.Equal(t, tc.expectedName, name)
assert.Equal(t, tc.expectedLabel, label)

// check that the output is consistent (e.g. after ReplicaSet owner data is cached)
name, label = tc.owner.TopOwnerNameLabel()
assert.Equal(t, tc.expectedName, name)
assert.Equal(t, tc.expectedLabel, label)
})
}
}
2 changes: 1 addition & 1 deletion pkg/internal/netolly/ebpf/net_arm64_bpfel.o
Git LFS file not shown
2 changes: 1 addition & 1 deletion pkg/internal/netolly/ebpf/netsk_x86_bpfel.o
Git LFS file not shown
8 changes: 4 additions & 4 deletions pkg/transform/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ func (md *metadataDecorator) appendMetadata(span *request.Span, info *kube.PodIn
attr.K8sPodStartTime: info.StartTimeStr,
attr.K8sClusterName: md.clusterName,
}
owner := info.Owner
for owner != nil {
span.ServiceID.Metadata[attr.Name(owner.LabelName)] = owner.Name
owner = owner.Owner
if info.Owner != nil {
span.ServiceID.Metadata[attr.Name(info.Owner.LabelName)] = info.Owner.Name
topName, topLabel := info.Owner.TopOwnerNameLabel()
span.ServiceID.Metadata[attr.Name(topLabel)] = topName
}
// override hostname by the Pod name
span.ServiceID.HostName = info.Name
Expand Down
3 changes: 2 additions & 1 deletion pkg/transform/k8s_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ func TestDecoration(t *testing.T) {
deco := testutil.ReadChannel(t, outputhCh, timeout)
require.Len(t, deco, 1)
assert.Equal(t, "the-ns", deco[0].ServiceID.Namespace)
assert.Equal(t, "rs-34", deco[0].ServiceID.Name)
assert.Equal(t, "rs", deco[0].ServiceID.Name)
assert.Equal(t, map[attr.Name]string{
"k8s.node.name": "the-node",
"k8s.namespace.name": "the-ns",
"k8s.replicaset.name": "rs-34",
"k8s.deployment.name": "rs",
"k8s.pod.name": "pod-34",
"k8s.pod.uid": "uid-34",
"k8s.pod.start_time": "2020-01-02 12:34:56",
Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/beyla/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Development version of the beyla Dockerfile that compiles for coverage
# and allows retrieving coverage files.
# The production-ready minimal image is in the project root's dockerfile.
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/beyla/Dockerfile_dbg
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Development version of the beyla Dockerfile that compiles for coverage
# and allows retrieving coverage files.
# The production-ready minimal image is in the project root's dockerfile.
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/dotnetserver/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:7.0 as builder
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS builder

# Set the working directory to /build
WORKDIR /build
Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/gohttp2/client/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build the testserver binary
# Docker command must be invoked from the projec root directory
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/gohttp2/server/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build the testserver binary
# Docker command must be invoked from the projec root directory
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/gokafka/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build the testserver binary
# Docker command must be invoked from the projec root directory
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/goredis/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build the testserver binary
# Docker command must be invoked from the projec root directory
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/grpcpinger/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build the testserver binary
# Docker command must be invoked from the projec root directory
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/httppinger/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build the testserver binary
# Docker command must be invoked from the projec root directory
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/old_grpc/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

WORKDIR /src

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/old_grpc/worker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

WORKDIR /src

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/pingclient/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build the testserver binary
# Docker command must be invoked from the projec root directory
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
2 changes: 1 addition & 1 deletion test/integration/components/sqlclient/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build the testserver binary
# Docker command must be invoked from the projec root directory
FROM golang:1.23 as builder
FROM golang:1.23 AS builder

ARG TARGETARCH

Expand Down
Loading

0 comments on commit 9d41b5c

Please sign in to comment.