From 010f0bd02a8097d01dabd3de91508d73c022a792 Mon Sep 17 00:00:00 2001 From: Jennifer Chen <32009013+jennchenn@users.noreply.github.com> Date: Mon, 13 May 2024 10:15:50 -0400 Subject: [PATCH] [kubeapiserver/events] Support namespace labels as tags on kubernetes events (#25490) * Add namespace info to workloadmeta * Add namespace label tags to kubernetes events * Unit test adding namespace tags * Format code * Use namespace workloadmeta info to get pod labels * Remove apiserver namespace informer factory * Add changelog entry * Minor updates to get ns label API call * Do not fast return namespace labels if namespace collection disabled * Use assert.ElementsMatch and add comment in events test --- .../api/v1/kubernetes_metadata.go | 9 +- .../collectors/workloadmeta_extract.go | 26 +++++- .../collectors/workloadmeta_test.go | 91 ++++++++++++++++++- .../internal/kubeapiserver/reflector_store.go | 2 + .../internal/kubemetadata/kubemetadata.go | 15 ++- .../kubemetadata/kubemetadata_test.go | 34 +------ .../kubernetesapiserver/bundled_events.go | 14 ++- .../kubernetesapiserver/events_common.go | 13 ++- .../kubernetesapiserver/events_common_test.go | 55 ++++++++++- .../kubernetes_apiserver.go | 2 +- .../kubernetes_eventbundle.go | 5 +- .../kubernetes_eventbundle_test.go | 5 +- .../kubernetesapiserver/unbundled_events.go | 10 +- .../unbundled_events_test.go | 56 ++++++++++++ pkg/util/clusteragent/clusteragent.go | 3 +- pkg/util/kubernetes/apiserver/controllers.go | 1 - .../apiserver/metadata_controller.go | 30 +----- .../apiserver/metadata_controller_test.go | 1 - ...ith-namespace-labels-eb958ece9487e5ee.yaml | 11 +++ 19 files changed, 291 insertions(+), 92 deletions(-) create mode 100644 releasenotes-dca/notes/tag-kubernetes-events-with-namespace-labels-eb958ece9487e5ee.yaml diff --git a/cmd/cluster-agent/api/v1/kubernetes_metadata.go b/cmd/cluster-agent/api/v1/kubernetes_metadata.go index b2f9f5aad43aa..b121d071a71a6 100644 --- a/cmd/cluster-agent/api/v1/kubernetes_metadata.go +++ b/cmd/cluster-agent/api/v1/kubernetes_metadata.go @@ -36,7 +36,7 @@ func installKubernetesMetadataEndpoints(r *mux.Router, wmeta workloadmeta.Compon "getNodeLabels", func(w http.ResponseWriter, r *http.Request) { getNodeLabels(w, r, wmeta) }, )).Methods("GET") - r.HandleFunc("/tags/namespace/{ns}", api.WithTelemetryWrapper("getNamespaceLabels", getNamespaceLabels)).Methods("GET") + r.HandleFunc("/tags/namespace/{ns}", api.WithTelemetryWrapper("getNamespaceLabels", func(w http.ResponseWriter, r *http.Request) { getNamespaceLabels(w, r, wmeta) })).Methods("GET") r.HandleFunc("/cluster/id", api.WithTelemetryWrapper("getClusterID", getClusterID)).Methods("GET") } @@ -110,7 +110,7 @@ func getNodeAnnotations(w http.ResponseWriter, r *http.Request, wmeta workloadme } // getNamespaceLabels is only used when the node agent hits the DCA for the list of labels -func getNamespaceLabels(w http.ResponseWriter, r *http.Request) { +func getNamespaceLabels(w http.ResponseWriter, r *http.Request, wmeta workloadmeta.Component) { /* Input localhost:5001/api/v1/tags/namespace/default @@ -131,12 +131,13 @@ func getNamespaceLabels(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) var labelBytes []byte nsName := vars["ns"] - nsLabels, err := as.GetNamespaceLabels(nsName) + namespace, err := wmeta.GetKubernetesNamespace(nsName) if err != nil { - log.Errorf("Could not retrieve the namespace labels of %s: %v", nsName, err.Error()) //nolint:errcheck + log.Errorf("Could not retrieve the namespace %s: %v", nsName, err.Error()) //nolint:errcheck http.Error(w, err.Error(), http.StatusInternalServerError) return } + nsLabels := namespace.Labels labelBytes, err = json.Marshal(nsLabels) if err != nil { log.Errorf("Could not process the labels of the namespace %s from the informer's cache: %v", nsName, err.Error()) //nolint:errcheck diff --git a/comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go b/comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go index 3dec15310011e..47671a25b3dfb 100644 --- a/comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go +++ b/comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go @@ -137,7 +137,7 @@ func (c *WorkloadMetaCollector) processEvents(evBundle workloadmeta.EventBundle) case workloadmeta.KindKubernetesNode: tagInfos = append(tagInfos, c.handleKubeNode(ev)...) case workloadmeta.KindKubernetesNamespace: - // tagInfos = append(tagInfos, c.handleKubeNamespace(ev)...) No tags for now + tagInfos = append(tagInfos, c.handleKubeNamespace(ev)...) case workloadmeta.KindECSTask: tagInfos = append(tagInfos, c.handleECSTask(ev)...) case workloadmeta.KindContainerImageMetadata: @@ -437,6 +437,30 @@ func (c *WorkloadMetaCollector) handleKubeNode(ev workloadmeta.Event) []*types.T return tagInfos } +func (c *WorkloadMetaCollector) handleKubeNamespace(ev workloadmeta.Event) []*types.TagInfo { + namespace := ev.Entity.(*workloadmeta.KubernetesNamespace) + + tags := utils.NewTagList() + + for name, value := range namespace.Labels { + utils.AddMetadataAsTags(name, value, c.nsLabelsAsTags, c.globNsLabels, tags) + } + + low, orch, high, standard := tags.Compute() + tagInfos := []*types.TagInfo{ + { + Source: nodeSource, + Entity: buildTaggerEntityID(namespace.EntityID), + HighCardTags: high, + OrchestratorCardTags: orch, + LowCardTags: low, + StandardTags: standard, + }, + } + + return tagInfos +} + func (c *WorkloadMetaCollector) handleECSTask(ev workloadmeta.Event) []*types.TagInfo { task := ev.Entity.(*workloadmeta.ECSTask) diff --git a/comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go b/comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go index 55a6b9405e096..7e4741b411557 100644 --- a/comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go +++ b/comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go @@ -11,6 +11,8 @@ import ( "sort" "testing" + "go.uber.org/fx" + "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/log/logimpl" "github.com/DataDog/datadog-agent/comp/core/tagger/types" @@ -18,7 +20,6 @@ import ( "github.com/DataDog/datadog-agent/comp/core/workloadmeta" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/kubernetes" - "go.uber.org/fx" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" @@ -606,6 +607,94 @@ func TestHandleKubePodNoContainerName(t *testing.T) { } } +func TestHandleKubeNamespace(t *testing.T) { + const namespace = "foobar" + + namespaceEntityID := workloadmeta.EntityID{ + Kind: workloadmeta.KindKubernetesNamespace, + ID: namespace, + } + + namespaceTaggerEntityID := fmt.Sprintf("namespace://%s", namespaceEntityID.ID) + + store := fxutil.Test[workloadmeta.Mock](t, fx.Options( + logimpl.MockModule(), + config.MockModule(), + fx.Supply(workloadmeta.NewParams()), + fx.Supply(context.Background()), + workloadmeta.MockModule(), + )) + + store.Set(&workloadmeta.Container{ + EntityID: workloadmeta.EntityID{ + Kind: workloadmeta.KindKubernetesNamespace, + ID: namespace, + }, + EntityMeta: workloadmeta.EntityMeta{ + Name: namespace, + }, + }) + + tests := []struct { + name string + labelsAsTags map[string]string + annotationsAsTags map[string]string + nsLabelsAsTags map[string]string + namespace workloadmeta.KubernetesNamespace + expected []*types.TagInfo + }{ + { + name: "fully formed namespace", + nsLabelsAsTags: map[string]string{ + "ns_env": "ns_env", + "ns-ownerteam": "ns-team", + }, + namespace: workloadmeta.KubernetesNamespace{ + EntityID: namespaceEntityID, + EntityMeta: workloadmeta.EntityMeta{ + Name: namespace, + Labels: map[string]string{ + "ns_env": "dev", + "ns-ownerteam": "containers", + "foo": "bar", + }, + }, + }, + expected: []*types.TagInfo{ + { + Source: nodeSource, + Entity: namespaceTaggerEntityID, + HighCardTags: []string{}, + OrchestratorCardTags: []string{}, + LowCardTags: []string{ + "ns_env:dev", + "ns-team:containers", + }, + StandardTags: []string{}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + collector := &WorkloadMetaCollector{ + store: store, + children: make(map[string]map[string]struct{}), + } + + collector.initPodMetaAsTags(tt.labelsAsTags, tt.annotationsAsTags, tt.nsLabelsAsTags) + + actual := collector.handleKubeNamespace(workloadmeta.Event{ + Type: workloadmeta.EventTypeSet, + Entity: &tt.namespace, + }) + + assertTagInfoListEqual(t, tt.expected, actual) + }) + } +} + func TestHandleECSTask(t *testing.T) { const ( containerID = "foobarquux" diff --git a/comp/core/workloadmeta/collectors/internal/kubeapiserver/reflector_store.go b/comp/core/workloadmeta/collectors/internal/kubeapiserver/reflector_store.go index 5ea2171d335d3..f605ac2419a1c 100644 --- a/comp/core/workloadmeta/collectors/internal/kubeapiserver/reflector_store.go +++ b/comp/core/workloadmeta/collectors/internal/kubeapiserver/reflector_store.go @@ -150,6 +150,8 @@ func (r *reflectorStore) Delete(obj interface{}) error { uid = v.UID case *appsv1.Deployment: uid = v.UID + case *corev1.Namespace: + uid = v.UID default: return fmt.Errorf("failed to identify Kind of object: %#v", obj) } diff --git a/comp/core/workloadmeta/collectors/internal/kubemetadata/kubemetadata.go b/comp/core/workloadmeta/collectors/internal/kubemetadata/kubemetadata.go index ca09ff8cf8c25..b0e701e5dbd43 100644 --- a/comp/core/workloadmeta/collectors/internal/kubemetadata/kubemetadata.go +++ b/comp/core/workloadmeta/collectors/internal/kubemetadata/kubemetadata.go @@ -16,6 +16,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" + "go.uber.org/fx" + "github.com/DataDog/datadog-agent/comp/core/workloadmeta" apiv1 "github.com/DataDog/datadog-agent/pkg/clusteragent/api/v1" "github.com/DataDog/datadog-agent/pkg/config" @@ -25,7 +27,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/util/kubernetes/kubelet" "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/retry" - "go.uber.org/fx" ) const ( @@ -229,7 +230,7 @@ func (c *collector) parsePods( } var nsLabels map[string]string - nsLabels, err = c.getNamespaceLabels(apiserver.GetNamespaceLabels, pod.Metadata.Namespace) + nsLabels, err = c.getNamespaceLabels(pod.Metadata.Namespace) if err != nil { log.Debugf("Could not fetch namespace labels for pod %s/%s: %v", pod.Metadata.Namespace, pod.Metadata.Name, err) } @@ -289,16 +290,12 @@ func (c *collector) getMetadata(getPodMetaDataFromAPIServerFunc func(string, str } // getNamespaceLabels returns the namespace labels, fast return if namespace labels as tags is disabled. -func (c *collector) getNamespaceLabels(getNamespaceLabelsFromAPIServerFunc func(string) (map[string]string, error), ns string) (map[string]string, error) { - if !c.collectNamespaceLabels { +func (c *collector) getNamespaceLabels(ns string) (map[string]string, error) { + if !c.collectNamespaceLabels || !c.isDCAEnabled() { return nil, nil } - if c.isDCAEnabled() { - getNamespaceLabelsFromAPIServerFunc = c.dcaClient.GetNamespaceLabels - } - - return getNamespaceLabelsFromAPIServerFunc(ns) + return c.dcaClient.GetNamespaceLabels(ns) } func (c *collector) isDCAEnabled() bool { diff --git a/comp/core/workloadmeta/collectors/internal/kubemetadata/kubemetadata_test.go b/comp/core/workloadmeta/collectors/internal/kubemetadata/kubemetadata_test.go index 6fb439f347c14..d2f67797fa846 100644 --- a/comp/core/workloadmeta/collectors/internal/kubemetadata/kubemetadata_test.go +++ b/comp/core/workloadmeta/collectors/internal/kubemetadata/kubemetadata_test.go @@ -296,14 +296,10 @@ func TestKubeMetadataCollector_getNamespaceLabels(t *testing.T) { dcaClient clusteragent.DCAClientInterface clusterAgentEnabled bool } - type args struct { - getNamespaceLabelsFromAPIServerFunc func(string) (map[string]string, error) - } tests := []struct { name string fields fields - args args namespaceLabelsAsTags map[string]string want map[string]string wantErr bool @@ -315,30 +311,6 @@ func TestKubeMetadataCollector_getNamespaceLabels(t *testing.T) { }, { name: "cluster agent not enabled", - args: args{ - getNamespaceLabelsFromAPIServerFunc: func(string) (map[string]string, error) { - return map[string]string{ - "label": "value", - }, nil - }, - }, - fields: fields{ - clusterAgentEnabled: false, - dcaClient: &FakeDCAClient{}, - }, - namespaceLabelsAsTags: map[string]string{ - "label": "tag", - }, - want: map[string]string{"label": "tag"}, - wantErr: false, - }, - { - name: "cluster agent not enabled and failed to get namespace labels", - args: args{ - getNamespaceLabelsFromAPIServerFunc: func(string) (map[string]string, error) { - return nil, errors.New("failed to get namespace labels") - }, - }, fields: fields{ clusterAgentEnabled: false, dcaClient: &FakeDCAClient{}, @@ -347,11 +319,10 @@ func TestKubeMetadataCollector_getNamespaceLabels(t *testing.T) { "label": "tag", }, want: nil, - wantErr: true, + wantErr: false, }, { name: "cluster agent enabled", - args: args{}, fields: fields{ clusterAgentEnabled: true, dcaClient: &FakeDCAClient{ @@ -369,7 +340,6 @@ func TestKubeMetadataCollector_getNamespaceLabels(t *testing.T) { }, { name: "cluster agent enabled and failed to get namespace labels", - args: args{}, fields: fields{ clusterAgentEnabled: true, dcaClient: &FakeDCAClient{ @@ -392,7 +362,7 @@ func TestKubeMetadataCollector_getNamespaceLabels(t *testing.T) { collectNamespaceLabels: len(tt.namespaceLabelsAsTags) > 0, } - labels, err := c.getNamespaceLabels(tt.args.getNamespaceLabelsFromAPIServerFunc, "foo") + labels, err := c.getNamespaceLabels("foo") assert.True(t, (err != nil) == tt.wantErr) assert.EqualValues(&testing.T{}, tt.want, labels) }) diff --git a/pkg/collector/corechecks/cluster/kubernetesapiserver/bundled_events.go b/pkg/collector/corechecks/cluster/kubernetesapiserver/bundled_events.go index fed11d0a7fd17..9d05d3aa15676 100644 --- a/pkg/collector/corechecks/cluster/kubernetesapiserver/bundled_events.go +++ b/pkg/collector/corechecks/cluster/kubernetesapiserver/bundled_events.go @@ -8,18 +8,22 @@ package kubernetesapiserver import ( - "github.com/DataDog/datadog-agent/pkg/metrics/event" v1 "k8s.io/api/core/v1" + + "github.com/DataDog/datadog-agent/comp/core/tagger" + "github.com/DataDog/datadog-agent/pkg/metrics/event" ) -func newBundledTransformer(clusterName string) eventTransformer { +func newBundledTransformer(clusterName string, taggerInstance tagger.Component) eventTransformer { return &bundledTransformer{ - clusterName: clusterName, + clusterName: clusterName, + taggerInstance: taggerInstance, } } type bundledTransformer struct { - clusterName string + clusterName string + taggerInstance tagger.Component } func (c *bundledTransformer) Transform(events []*v1.Event) ([]event.Event, []error) { @@ -60,7 +64,7 @@ func (c *bundledTransformer) Transform(events []*v1.Event) ([]event.Event, []err datadogEvs := make([]event.Event, 0, len(bundlesByObject)) for id, bundle := range bundlesByObject { - datadogEv, err := bundle.formatEvents() + datadogEv, err := bundle.formatEvents(c.taggerInstance) if err != nil { errors = append(errors, err) continue diff --git a/pkg/collector/corechecks/cluster/kubernetesapiserver/events_common.go b/pkg/collector/corechecks/cluster/kubernetesapiserver/events_common.go index 8336ecc809c30..9a3a1b76296c3 100644 --- a/pkg/collector/corechecks/cluster/kubernetesapiserver/events_common.go +++ b/pkg/collector/corechecks/cluster/kubernetesapiserver/events_common.go @@ -14,11 +14,14 @@ import ( v1 "k8s.io/api/core/v1" + "github.com/patrickmn/go-cache" + + "github.com/DataDog/datadog-agent/comp/core/tagger" + "github.com/DataDog/datadog-agent/comp/core/tagger/types" "github.com/DataDog/datadog-agent/pkg/metrics/event" "github.com/DataDog/datadog-agent/pkg/util/kubernetes" "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver" "github.com/DataDog/datadog-agent/pkg/util/log" - "github.com/patrickmn/go-cache" ) var ( @@ -44,7 +47,7 @@ func getDDAlertType(k8sType string) event.EventAlertType { } } -func getInvolvedObjectTags(involvedObject v1.ObjectReference) []string { +func getInvolvedObjectTags(involvedObject v1.ObjectReference, taggerInstance tagger.Component) []string { // NOTE: we now standardized on using kube_* tags, instead of // non-namespaced ones, or kubernetes_*. The latter two are now // considered deprecated. @@ -64,6 +67,12 @@ func getInvolvedObjectTags(involvedObject v1.ObjectReference) []string { // DEPRECATED: fmt.Sprintf("namespace:%s", involvedObject.Namespace), ) + + namespaceEntityID := fmt.Sprintf("namespace://%s", involvedObject.Namespace) + namespaceEntity, err := taggerInstance.GetEntity(namespaceEntityID) + if err == nil { + tags = append(tags, namespaceEntity.GetTags(types.HighCardinality)...) + } } kindTag := getKindTag(involvedObject.Kind, involvedObject.Name) diff --git a/pkg/collector/corechecks/cluster/kubernetesapiserver/events_common_test.go b/pkg/collector/corechecks/cluster/kubernetesapiserver/events_common_test.go index c3cb28bb0ae7b..f2e7c88411290 100644 --- a/pkg/collector/corechecks/cluster/kubernetesapiserver/events_common_test.go +++ b/pkg/collector/corechecks/cluster/kubernetesapiserver/events_common_test.go @@ -12,9 +12,11 @@ import ( "reflect" "testing" - "github.com/DataDog/datadog-agent/pkg/metrics/event" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" + + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl/local" + "github.com/DataDog/datadog-agent/pkg/metrics/event" ) func TestGetDDAlertType(t *testing.T) { @@ -47,6 +49,57 @@ func TestGetDDAlertType(t *testing.T) { } } +func Test_getInvolvedObjectTags(t *testing.T) { + taggerInstance := local.NewFakeTagger() + taggerInstance.SetTags("namespace://default", "workloadmeta-kubernetes_node", []string{"team:container-int"}, nil, nil, nil) + tests := []struct { + name string + involvedObject v1.ObjectReference + tags []string + }{ + { + name: "get pod basic tags", + involvedObject: v1.ObjectReference{ + Kind: "Pod", + Name: "my-pod", + Namespace: "my-namespace", + }, + tags: []string{ + "kube_kind:Pod", + "kube_name:my-pod", + "kubernetes_kind:Pod", + "name:my-pod", + "kube_namespace:my-namespace", + "namespace:my-namespace", + "pod_name:my-pod", + }, + }, + { + name: "get pod namespace tags", + involvedObject: v1.ObjectReference{ + Kind: "Pod", + Name: "my-pod", + Namespace: "default", + }, + tags: []string{ + "kube_kind:Pod", + "kube_name:my-pod", + "kubernetes_kind:Pod", + "name:my-pod", + "kube_namespace:default", + "namespace:default", + "team:container-int", // this tag is coming from the namespace + "pod_name:my-pod", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.ElementsMatch(t, getInvolvedObjectTags(tt.involvedObject, taggerInstance), tt.tags) + }) + } +} + func Test_getEventHostInfoImpl(t *testing.T) { providerIDFunc := func(clusterName string) string { return fmt.Sprintf("foo-%s", clusterName) } diff --git a/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_apiserver.go b/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_apiserver.go index 5635803a5354c..39b5aa6e0e804 100644 --- a/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_apiserver.go +++ b/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_apiserver.go @@ -170,7 +170,7 @@ func (k *KubeASCheck) Configure(senderManager sender.SenderManager, integrationC k.eventCollection.Transformer = newUnbundledTransformer(clusterName, tagger.GetTaggerInstance(), k.instance.CollectedEventTypes) } else { k.eventCollection.Filter = convertFilters(k.instance.FilteredEventTypes) - k.eventCollection.Transformer = newBundledTransformer(clusterName) + k.eventCollection.Transformer = newBundledTransformer(clusterName, tagger.GetTaggerInstance()) } return nil diff --git a/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_eventbundle.go b/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_eventbundle.go index 069159d77292b..687bc2b260a37 100644 --- a/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_eventbundle.go +++ b/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_eventbundle.go @@ -17,6 +17,7 @@ import ( v1 "k8s.io/api/core/v1" + "github.com/DataDog/datadog-agent/comp/core/tagger" "github.com/DataDog/datadog-agent/pkg/metrics/event" ) @@ -55,13 +56,13 @@ func (b *kubernetesEventBundle) addEvent(event *v1.Event) error { return nil } -func (b *kubernetesEventBundle) formatEvents() (event.Event, error) { +func (b *kubernetesEventBundle) formatEvents(taggerInstance tagger.Component) (event.Event, error) { if len(b.countByAction) == 0 { return event.Event{}, errors.New("no event to export") } readableKey := buildReadableKey(b.involvedObject) - tags := getInvolvedObjectTags(b.involvedObject) + tags := getInvolvedObjectTags(b.involvedObject, taggerInstance) tags = append(tags, fmt.Sprintf("source_component:%s", b.component)) if b.hostInfo.providerID != "" { diff --git a/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_eventbundle_test.go b/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_eventbundle_test.go index 2437a0dae95a8..43a9a4020913b 100644 --- a/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_eventbundle_test.go +++ b/pkg/collector/corechecks/cluster/kubernetesapiserver/kubernetes_eventbundle_test.go @@ -17,6 +17,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl/local" "github.com/DataDog/datadog-agent/pkg/metrics/event" ) @@ -177,7 +178,7 @@ func TestFormatEvent(t *testing.T) { b.addEvent(ev) } - output, err := b.formatEvents() + output, err := b.formatEvents(local.NewFakeTagger()) assert.Nil(t, err) assert.Equal(t, tt.expected.Text, output.Text) @@ -239,7 +240,7 @@ func TestEventsTagging(t *testing.T) { t.Run(tt.name, func(t *testing.T) { bundle := newKubernetesEventBundler("", tt.k8sEvent) bundle.addEvent(tt.k8sEvent) - got, err := bundle.formatEvents() + got, err := bundle.formatEvents(local.NewFakeTagger()) assert.NoError(t, err) assert.ElementsMatch(t, tt.expectedTags, got.Tags) }) diff --git a/pkg/collector/corechecks/cluster/kubernetesapiserver/unbundled_events.go b/pkg/collector/corechecks/cluster/kubernetesapiserver/unbundled_events.go index b953a387c175f..2da68a1cb7840 100644 --- a/pkg/collector/corechecks/cluster/kubernetesapiserver/unbundled_events.go +++ b/pkg/collector/corechecks/cluster/kubernetesapiserver/unbundled_events.go @@ -66,7 +66,7 @@ func (c *unbundledTransformer) Transform(events []*v1.Event) ([]event.Event, []e readableKey := buildReadableKey(involvedObject) tagsAccumulator := tagset.NewHashlessTagsAccumulator() - tagsAccumulator.Append(getInvolvedObjectTags(involvedObject)...) + tagsAccumulator.Append(getInvolvedObjectTags(involvedObject, c.taggerInstance)...) tagsAccumulator.Append( fmt.Sprintf("source_component:%s", ev.Source.Component), fmt.Sprintf("event_reason:%s", ev.Reason)) @@ -111,6 +111,14 @@ func (c *unbundledTransformer) getTagsFromTagger(obj v1.ObjectReference, tagsAcc } // we can get high Cardinality because tags on events is seemless. tagsAcc.Append(entity.GetTags(types.HighCardinality)...) + + namespaceEntityID := fmt.Sprintf("namespace://%s", obj.Namespace) + namespaceEntity, err := c.taggerInstance.GetEntity(namespaceEntityID) + if err != nil { + return + } + tagsAcc.Append(namespaceEntity.GetTags(types.HighCardinality)...) + default: return } diff --git a/pkg/collector/corechecks/cluster/kubernetesapiserver/unbundled_events_test.go b/pkg/collector/corechecks/cluster/kubernetesapiserver/unbundled_events_test.go index 937fba944d4c6..a07d5a03145e9 100644 --- a/pkg/collector/corechecks/cluster/kubernetesapiserver/unbundled_events_test.go +++ b/pkg/collector/corechecks/cluster/kubernetesapiserver/unbundled_events_test.go @@ -17,6 +17,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl/local" "github.com/DataDog/datadog-agent/pkg/metrics/event" + "github.com/DataDog/datadog-agent/pkg/tagset" ) func TestUnbundledEventsTransform(t *testing.T) { @@ -107,6 +108,61 @@ func TestUnbundledEventsTransform(t *testing.T) { } } +func TestGetTagsFromTagger(t *testing.T) { + taggerInstance := local.NewFakeTagger() + taggerInstance.SetTags("kubernetes_pod_uid://nginx", "workloadmeta-kubernetes_pod", nil, []string{"pod_name:nginx"}, nil, nil) + taggerInstance.SetTags("namespace://foobar", "workloadmeta-kubernetes_node", []string{"team:container-int"}, nil, nil, nil) + + tests := []struct { + name string + obj v1.ObjectReference + expectedTags *tagset.HashlessTagsAccumulator + }{ + { + name: "accumulates basic pod tags", + obj: v1.ObjectReference{ + UID: "redis", + Kind: "Pod", + Namespace: "default", + Name: "redis", + }, + expectedTags: tagset.NewHashlessTagsAccumulator(), + }, + { + name: "add tagger pod tags", + obj: v1.ObjectReference{ + UID: "nginx", + Kind: "Pod", + Namespace: "default", + Name: "nginx", + }, + expectedTags: tagset.NewHashlessTagsAccumulatorFromSlice([]string{"pod_name:nginx"}), + }, + { + name: "add tagger namespace tags", + obj: v1.ObjectReference{ + UID: "nginx", + Kind: "Pod", + Namespace: "foobar", + Name: "nginx", + }, + expectedTags: tagset.NewHashlessTagsAccumulatorFromSlice([]string{"pod_name:nginx", "team:container-int"}), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + collectedTypes := []collectedEventType{ + {Kind: "Pod", Reasons: []string{}}, + } + transformer := newUnbundledTransformer("test-cluster", taggerInstance, collectedTypes) + accumulator := tagset.NewHashlessTagsAccumulator() + transformer.(*unbundledTransformer).getTagsFromTagger(tt.obj, accumulator) + assert.Equal(t, tt.expectedTags, accumulator) + }) + } +} + func TestUnbundledEventsShouldCollect(t *testing.T) { tests := []struct { name string diff --git a/pkg/util/clusteragent/clusteragent.go b/pkg/util/clusteragent/clusteragent.go index b58b9c4ea7303..6697f183dbfd4 100644 --- a/pkg/util/clusteragent/clusteragent.go +++ b/pkg/util/clusteragent/clusteragent.go @@ -20,6 +20,8 @@ import ( "sync" "time" + "google.golang.org/protobuf/proto" + "github.com/DataDog/datadog-agent/pkg/api/security" apiv1 "github.com/DataDog/datadog-agent/pkg/clusteragent/api/v1" "github.com/DataDog/datadog-agent/pkg/clusteragent/clusterchecks/types" @@ -29,7 +31,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/retry" "github.com/DataDog/datadog-agent/pkg/version" - "google.golang.org/protobuf/proto" ) /* diff --git a/pkg/util/kubernetes/apiserver/controllers.go b/pkg/util/kubernetes/apiserver/controllers.go index 90c9511138ac7..bebabb604dd46 100644 --- a/pkg/util/kubernetes/apiserver/controllers.go +++ b/pkg/util/kubernetes/apiserver/controllers.go @@ -119,7 +119,6 @@ func StartControllers(ctx ControllerContext) errors.Aggregate { func startMetadataController(ctx ControllerContext, c chan error) { metaController := NewMetadataController( ctx.InformerFactory.Core().V1().Nodes(), - ctx.InformerFactory.Core().V1().Namespaces(), ctx.InformerFactory.Core().V1().Endpoints(), ) go metaController.Run(ctx.StopCh) diff --git a/pkg/util/kubernetes/apiserver/metadata_controller.go b/pkg/util/kubernetes/apiserver/metadata_controller.go index ace99fc2d8893..e4f8ffa05ab91 100644 --- a/pkg/util/kubernetes/apiserver/metadata_controller.go +++ b/pkg/util/kubernetes/apiserver/metadata_controller.go @@ -11,7 +11,6 @@ import ( "fmt" "time" - "github.com/DataDog/datadog-agent/pkg/config" agentcache "github.com/DataDog/datadog-agent/pkg/util/cache" "github.com/DataDog/datadog-agent/pkg/util/log" @@ -37,8 +36,6 @@ type MetadataController struct { nodeLister corelisters.NodeLister nodeListerSynced cache.InformerSynced - namespaceListerSynced cache.InformerSynced - endpointsLister corelisters.EndpointsLister endpointsListerSynced cache.InformerSynced @@ -49,7 +46,7 @@ type MetadataController struct { } // NewMetadataController returns a new metadata controller -func NewMetadataController(nodeInformer coreinformers.NodeInformer, namespaceInformer coreinformers.NamespaceInformer, endpointsInformer coreinformers.EndpointsInformer) *MetadataController { +func NewMetadataController(nodeInformer coreinformers.NodeInformer, endpointsInformer coreinformers.EndpointsInformer) *MetadataController { m := &MetadataController{ queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "endpoints"), } @@ -62,8 +59,6 @@ func NewMetadataController(nodeInformer coreinformers.NodeInformer, namespaceInf m.nodeLister = nodeInformer.Lister() m.nodeListerSynced = nodeInformer.Informer().HasSynced - m.namespaceListerSynced = namespaceInformer.Informer().HasSynced - if _, err := endpointsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: m.addEndpoints, UpdateFunc: m.updateEndpoints, @@ -86,7 +81,7 @@ func (m *MetadataController) Run(stopCh <-chan struct{}) { log.Infof("Starting metadata controller") defer log.Infof("Stopping metadata controller") - if !cache.WaitForCacheSync(stopCh, m.nodeListerSynced, m.namespaceListerSynced, m.endpointsListerSynced) { + if !cache.WaitForCacheSync(stopCh, m.nodeListerSynced, m.endpointsListerSynced) { return } @@ -319,24 +314,3 @@ func GetPodMetadataNames(nodeName, ns, podName string) ([]string, error) { } return metaList, nil } - -// GetNamespaceLabels retrieves the labels of the queried namespace from the cache of the shared informer. -func GetNamespaceLabels(nsName string) (map[string]string, error) { - if !config.Datadog.GetBool("kubernetes_collect_metadata_tags") { - return nil, log.Errorf("Metadata collection is disabled on the Cluster Agent") - } - - as, err := GetAPIClient() - if err != nil { - return nil, err - } - - ns, err := as.InformerFactory.Core().V1().Namespaces().Lister().Get(nsName) - if err != nil { - return nil, err - } - if ns == nil { - return nil, fmt.Errorf("cannot get namespace %s from the informer's cache", nsName) - } - return ns.Labels, nil -} diff --git a/pkg/util/kubernetes/apiserver/metadata_controller_test.go b/pkg/util/kubernetes/apiserver/metadata_controller_test.go index ab8724fc1c0fa..fc268c6759a23 100644 --- a/pkg/util/kubernetes/apiserver/metadata_controller_test.go +++ b/pkg/util/kubernetes/apiserver/metadata_controller_test.go @@ -472,7 +472,6 @@ func newFakeMetadataController(client kubernetes.Interface) (*MetadataController metaController := NewMetadataController( informerFactory.Core().V1().Nodes(), - informerFactory.Core().V1().Namespaces(), informerFactory.Core().V1().Endpoints(), ) diff --git a/releasenotes-dca/notes/tag-kubernetes-events-with-namespace-labels-eb958ece9487e5ee.yaml b/releasenotes-dca/notes/tag-kubernetes-events-with-namespace-labels-eb958ece9487e5ee.yaml new file mode 100644 index 0000000000000..c97b099aefbd6 --- /dev/null +++ b/releasenotes-dca/notes/tag-kubernetes-events-with-namespace-labels-eb958ece9487e5ee.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +features: + - | + Support namespace labels as tags on kubernetes events.