Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[receiver/k8sobjects] ensure the k8s.namespace.name attribute is set for objects retrieved using the watch mode #36432

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d1a9be1
[receiver/k8sobjects]: add k8s.namespace attribute to watched objects…
bacherfl Nov 18, 2024
1c977d2
update expectations
bacherfl Nov 18, 2024
7466582
add changelog entry
bacherfl Nov 19, 2024
95d8413
fix changelog type
bacherfl Nov 19, 2024
9227b9d
fix linting
bacherfl Nov 19, 2024
691a8f4
fix unit test
bacherfl Nov 19, 2024
1f4c570
fix linting
bacherfl Nov 19, 2024
00d8482
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Nov 19, 2024
87423bc
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Nov 19, 2024
8f16f2d
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Nov 22, 2024
3c648f6
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Nov 26, 2024
3f483ac
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Nov 28, 2024
07a8851
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Dec 2, 2024
9ff388f
undo structural changes to log bodies
bacherfl Dec 3, 2024
5372fcf
undo changes to transform processor
bacherfl Dec 3, 2024
1baf90e
re-add newlines
bacherfl Dec 3, 2024
11f5383
adapt changelog entry
bacherfl Dec 3, 2024
15d7cc6
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Dec 4, 2024
3d57869
revert unneeded changes
bacherfl Dec 5, 2024
fbce0dc
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Dec 9, 2024
de12a7d
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Dec 10, 2024
40b828c
remove unused import
bacherfl Dec 10, 2024
c258815
Merge branch 'main' into fix/36352/k8sobjects-namespace
bacherfl Dec 19, 2024
5b405b5
Merge branch 'main' into fix/36352/k8sobjects-namespace
ChrsMark Dec 20, 2024
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
27 changes: 27 additions & 0 deletions .chloggen/k8sobjects-data-structure.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: bug_fix

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: k8sobjectsreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: ensure the `k8s.namespace.name` attribute is set for objects retrieved using the `watch` mode

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [36352]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
2 changes: 2 additions & 0 deletions receiver/k8sobjectsreceiver/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ func TestE2E(t *testing.T) {
}, time.Duration(tc.timeoutMinutes)*time.Minute, 1*time.Second,
"Timeout: failed to receive logs in %d minutes", tc.timeoutMinutes)

// golden.WriteLogs(t, expectedFile, logsConsumer.AllLogs()[0])

require.NoErrorf(t, plogtest.CompareLogs(expected, logsConsumer.AllLogs()[0],
plogtest.IgnoreObservedTimestamp(),
plogtest.IgnoreResourceLogsOrder(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
resourceLogs:
- resource: {}
- resource:
attributes:
- key: k8s.namespace.name
value:
stringValue: default
scopeLogs:
- scope: {}
logRecords:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
resourceLogs:
- resource: {}
- resource:
attributes:
- key: k8s.namespace.name
value:
stringValue: default
scopeLogs:
- scope: {}
logRecords:
Expand Down
18 changes: 15 additions & 3 deletions receiver/k8sobjectsreceiver/unstructured_to_logdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ func unstructuredListToLogData(event *unstructured.UnstructuredList, observedAt
namespaceResourceMap := make(map[string]plog.LogRecordSlice)

for _, e := range event.Items {
logSlice, ok := namespaceResourceMap[e.GetNamespace()]
logSlice, ok := namespaceResourceMap[getNamespace(e)]
if !ok {
rl := resourceLogs.AppendEmpty()
resourceAttrs := rl.Resource().Attributes()
if namespace := e.GetNamespace(); namespace != "" {
if namespace := getNamespace(e); namespace != "" {
resourceAttrs.PutStr(semconv.AttributeK8SNamespaceName, namespace)
}
sl := rl.ScopeLogs().AppendEmpty()
logSlice = sl.LogRecords()
namespaceResourceMap[e.GetNamespace()] = logSlice
namespaceResourceMap[getNamespace(e)] = logSlice
}
record := logSlice.AppendEmpty()
record.SetObservedTimestamp(pcommon.NewTimestampFromTime(observedAt))
Expand All @@ -79,3 +79,15 @@ func unstructuredListToLogData(event *unstructured.UnstructuredList, observedAt
}
return out
}

func getNamespace(e unstructured.Unstructured) string {
// first, try to use the GetNamespace() method, which checks for the metadata.namespace property
if namespace := e.GetNamespace(); namespace != "" {
return namespace
}
// try to look up namespace in object.metadata.namespace (for objects reported via watch mode)
if namespace, ok, _ := unstructured.NestedString(e.Object, "object", "metadata", "namespace"); ok {
return namespace
}
return ""
}
106 changes: 82 additions & 24 deletions receiver/k8sobjectsreceiver/unstructured_to_logdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
semconv "go.opentelemetry.io/collector/semconv/v1.27.0"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -91,7 +90,7 @@ func TestUnstructuredListToLogData(t *testing.T) {
assert.Equal(t, 3, logRecords.Len())
})

t.Run("Test event.name in watch events", func(t *testing.T) {
t.Run("Test event observed timestamp is present", func(t *testing.T) {
config := &K8sObjectsConfig{
gvr: &schema.GroupVersionResource{
Group: "",
Expand All @@ -112,7 +111,8 @@ func TestUnstructuredListToLogData(t *testing.T) {
},
}

logs, err := watchObjectsToLogData(event, time.Now(), config)
observedAt := time.Now()
logs, err := watchObjectsToLogData(event, observedAt, config)
assert.NoError(t, err)

assert.Equal(t, 1, logs.LogRecordCount())
Expand All @@ -123,47 +123,105 @@ func TestUnstructuredListToLogData(t *testing.T) {
logRecords := rl.ScopeLogs().At(0).LogRecords()
assert.Equal(t, 1, rl.ScopeLogs().Len())
assert.Equal(t, 1, logRecords.Len())

attrs := logRecords.At(0).Attributes()
eventName, ok := attrs.Get("event.name")
require.True(t, ok)
assert.EqualValues(t, "generic-name", eventName.AsRaw())
assert.Positive(t, logRecords.At(0).ObservedTimestamp().AsTime().Unix())
assert.Equal(t, logRecords.At(0).ObservedTimestamp().AsTime().Unix(), observedAt.Unix())
})

t.Run("Test event observed timestamp is present", func(t *testing.T) {
t.Run("Test pull and watch objects both contain k8s.namespace.name", func(t *testing.T) {
observedTimestamp := time.Now()
config := &K8sObjectsConfig{
gvr: &schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "events",
},
}
event := &watch.Event{
watchedEvent := &watch.Event{
Type: watch.Added,
Object: &unstructured.Unstructured{
Object: map[string]any{
"kind": "Event",
"apiVersion": "v1",
"metadata": map[string]any{
"name": "generic-name",
"name": "generic-name",
"namespace": "my-namespace",
},
},
},
}

observedAt := time.Now()
logs, err := watchObjectsToLogData(event, observedAt, config)
assert.NoError(t, err)

assert.Equal(t, 1, logs.LogRecordCount())
pulledEvent := &unstructured.UnstructuredList{
Items: []unstructured.Unstructured{{
Object: map[string]any{
"kind": "Event",
"apiVersion": "v1",
"metadata": map[string]any{
"name": "generic-name",
"namespace": "my-namespace",
},
},
}},
}

resourceLogs := logs.ResourceLogs()
assert.Equal(t, 1, resourceLogs.Len())
rl := resourceLogs.At(0)
logRecords := rl.ScopeLogs().At(0).LogRecords()
assert.Equal(t, 1, rl.ScopeLogs().Len())
assert.Equal(t, 1, logRecords.Len())
assert.Positive(t, logRecords.At(0).ObservedTimestamp().AsTime().Unix())
assert.Equal(t, logRecords.At(0).ObservedTimestamp().AsTime().Unix(), observedAt.Unix())
logEntryFromWatchEvent, err := watchObjectsToLogData(watchedEvent, observedTimestamp, config)
assert.NoError(t, err)
assert.NotNil(t, logEntryFromWatchEvent)

// verify the event.type, event.domain and k8s.resource.name attributes have been added

watchEventResourceAttrs := logEntryFromWatchEvent.ResourceLogs().At(0).Resource().Attributes()
k8sNamespace, ok := watchEventResourceAttrs.Get(semconv.AttributeK8SNamespaceName)
assert.True(t, ok)
assert.Equal(t,
"my-namespace",
k8sNamespace.Str(),
)

watchEvenLogRecordtAttrs := logEntryFromWatchEvent.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().At(0).Attributes()
eventType, ok := watchEvenLogRecordtAttrs.Get("event.name")
assert.True(t, ok)
assert.Equal(
t,
"generic-name",
eventType.AsString(),
)

eventDomain, ok := watchEvenLogRecordtAttrs.Get("event.domain")
assert.True(t, ok)
assert.Equal(
t,
"k8s",
eventDomain.AsString(),
)

k8sResourceName, ok := watchEvenLogRecordtAttrs.Get("k8s.resource.name")
assert.True(t, ok)
assert.Equal(
t,
"events",
k8sResourceName.AsString(),
)

logEntryFromPulledEvent := unstructuredListToLogData(pulledEvent, observedTimestamp, config)
assert.NotNil(t, logEntryFromPulledEvent)

pullEventResourceAttrs := logEntryFromPulledEvent.ResourceLogs().At(0).Resource().Attributes()
k8sNamespace, ok = pullEventResourceAttrs.Get(semconv.AttributeK8SNamespaceName)
assert.True(t, ok)
assert.Equal(
t,
"my-namespace",
k8sNamespace.Str(),
)

pullEventLogRecordAttrs := logEntryFromPulledEvent.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().At(0).Attributes()

k8sResourceName, ok = pullEventLogRecordAttrs.Get("k8s.resource.name")
assert.True(t, ok)
assert.Equal(
t,
"events",
k8sResourceName.AsString(),
)
})
}
Loading