diff --git a/.chloggen/k8sattr-rbac.yaml b/.chloggen/k8sattr-rbac.yaml new file mode 100644 index 000000000000..24077d3a066e --- /dev/null +++ b/.chloggen/k8sattr-rbac.yaml @@ -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: processor/k8sattributes + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Allows k8sattributes processor to work with k8s role/rolebindings when filter::namespace is set. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [14742] + +# (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: [user] diff --git a/internal/k8stest/client.go b/internal/k8stest/client.go new file mode 100644 index 000000000000..8481b37d62b9 --- /dev/null +++ b/internal/k8stest/client.go @@ -0,0 +1,46 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package k8stest // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8stest" + +import ( + "errors" + "fmt" + + "k8s.io/client-go/discovery" + memory "k8s.io/client-go/discovery/cached" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/restmapper" + "k8s.io/client-go/tools/clientcmd" +) + +type K8sClient struct { + DynamicClient *dynamic.DynamicClient + DiscoveryClient *discovery.DiscoveryClient + Mapper *restmapper.DeferredDiscoveryRESTMapper +} + +func NewK8sClient(kubeconfigPath string) (*K8sClient, error) { + + if kubeconfigPath == "" { + return nil, errors.New("Please provide file path to load kubeconfig") + } + restConfig, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + if err != nil { + return nil, fmt.Errorf("unable to load kubeconfig from %s: %w", kubeconfigPath, err) + } + + dynamicClient, err := dynamic.NewForConfig(restConfig) + if err != nil { + return nil, fmt.Errorf("error creating dynamic client: %w", err) + } + + discoveryClient, err := discovery.NewDiscoveryClientForConfig(restConfig) + if err != nil { + return nil, fmt.Errorf("error creating discovery client: %w", err) + } + + mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(discoveryClient)) + return &K8sClient{ + DynamicClient: dynamicClient, DiscoveryClient: discoveryClient, Mapper: mapper}, nil +} diff --git a/internal/k8stest/go.mod b/internal/k8stest/go.mod index 82bb6f7d7a7e..a2a5f70c2bba 100644 --- a/internal/k8stest/go.mod +++ b/internal/k8stest/go.mod @@ -16,19 +16,30 @@ require ( github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/go-logr/logr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.14.0 // indirect @@ -44,6 +55,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.4.0 // indirect k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect diff --git a/internal/k8stest/go.sum b/internal/k8stest/go.sum index e3b9698a9e09..179a7abc467c 100644 --- a/internal/k8stest/go.sum +++ b/internal/k8stest/go.sum @@ -2,6 +2,7 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,6 +16,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -23,6 +26,8 @@ github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2Kv github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -38,16 +43,23 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -63,6 +75,10 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= @@ -78,7 +94,12 @@ github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -144,6 +165,7 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= diff --git a/internal/k8stest/k8s_collector.go b/internal/k8stest/k8s_collector.go index 0530e9caeac3..d607fd194e1a 100644 --- a/internal/k8stest/k8s_collector.go +++ b/internal/k8stest/k8s_collector.go @@ -18,11 +18,12 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" ) -func CreateCollectorObjects(t *testing.T, client *dynamic.DynamicClient, testID string) []*unstructured.Unstructured { - manifestsDir := filepath.Join(".", "testdata", "e2e", "collector") +func CreateCollectorObjects(t *testing.T, client *K8sClient, testID string, manifestsDir string) []*unstructured.Unstructured { + if manifestsDir == "" { + manifestsDir = filepath.Join(".", "testdata", "e2e", "collector") + } manifestFiles, err := os.ReadDir(manifestsDir) require.NoErrorf(t, err, "failed to read collector manifests directory %s", manifestsDir) host := HostEndpoint(t) @@ -35,6 +36,7 @@ func CreateCollectorObjects(t *testing.T, client *dynamic.DynamicClient, testID require.NoError(t, tmpl.Execute(manifest, map[string]string{ "Name": "otelcol-" + testID, "HostEndpoint": host, + "TestID": testID, })) obj, err := CreateObject(client, manifest.Bytes()) require.NoErrorf(t, err, "failed to create collector object from manifest %s", manifestFile.Name()) @@ -52,13 +54,13 @@ func CreateCollectorObjects(t *testing.T, client *dynamic.DynamicClient, testID return createdObjs } -func WaitForCollectorToStart(t *testing.T, client *dynamic.DynamicClient, podNamespace string, podLabels map[string]any) { +func WaitForCollectorToStart(t *testing.T, client *K8sClient, podNamespace string, podLabels map[string]any) { podGVR := schema.GroupVersionResource{Version: "v1", Resource: "pods"} listOptions := metav1.ListOptions{LabelSelector: SelectorFromMap(podLabels).String()} podTimeoutMinutes := 3 t.Logf("waiting for collector pods to be ready") require.Eventuallyf(t, func() bool { - list, err := client.Resource(podGVR).Namespace(podNamespace).List(context.Background(), listOptions) + list, err := client.DynamicClient.Resource(podGVR).Namespace(podNamespace).List(context.Background(), listOptions) require.NoError(t, err, "failed to list collector pods") podsNotReady := len(list.Items) if podsNotReady == 0 { diff --git a/internal/k8stest/k8s_objects.go b/internal/k8stest/k8s_objects.go index bbbff8d6e56b..bf4b559d0994 100644 --- a/internal/k8stest/k8s_objects.go +++ b/internal/k8stest/k8s_objects.go @@ -5,43 +5,51 @@ package k8stest // import "github.com/open-telemetry/opentelemetry-collector-con import ( "context" - "strings" + "k8s.io/apimachinery/pkg/api/meta" 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/runtime/serializer/yaml" "k8s.io/client-go/dynamic" ) -func CreateObject(client *dynamic.DynamicClient, manifest []byte) (*unstructured.Unstructured, error) { +func CreateObject(client *K8sClient, manifest []byte) (*unstructured.Unstructured, error) { decoder := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) obj := &unstructured.Unstructured{} _, gvk, err := decoder.Decode(manifest, nil, obj) if err != nil { return nil, err } - gvr := schema.GroupVersionResource{ - Group: gvk.Group, - Version: gvk.Version, - Resource: strings.ToLower(gvk.Kind + "s"), + gvr, err := client.Mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + if err != nil { + return nil, err } - return client.Resource(gvr).Namespace(obj.GetNamespace()).Create(context.Background(), obj, metav1.CreateOptions{}) + var resource dynamic.ResourceInterface + if gvr.Scope.Name() == meta.RESTScopeNameNamespace { + resource = client.DynamicClient.Resource(gvr.Resource).Namespace(obj.GetNamespace()) + } else { + // cluster-scoped resources + resource = client.DynamicClient.Resource(gvr.Resource) + } + + return resource.Create(context.Background(), obj, metav1.CreateOptions{}) } -func DeleteObject(client *dynamic.DynamicClient, obj *unstructured.Unstructured) error { +func DeleteObject(client *K8sClient, obj *unstructured.Unstructured) error { gvk := obj.GroupVersionKind() - gvr := schema.GroupVersionResource{ - Group: gvk.Group, - Version: gvk.Version, - Resource: strings.ToLower(gvk.Kind + "s"), + gvr, err := client.Mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + if err != nil { + return err } - - options := metav1.DeleteOptions{} - policy := metav1.DeletePropagationBackground - if gvk.Kind == "Job" { - options.PropagationPolicy = &policy + var resource dynamic.ResourceInterface + if gvr.Scope.Name() == meta.RESTScopeNameNamespace { + resource = client.DynamicClient.Resource(gvr.Resource).Namespace(obj.GetNamespace()) + } else { + // cluster-scoped resources + resource = client.DynamicClient.Resource(gvr.Resource) } - - return client.Resource(gvr).Namespace(obj.GetNamespace()).Delete(context.Background(), obj.GetName(), options) + deletePolicy := metav1.DeletePropagationForeground + return resource.Delete(context.Background(), obj.GetName(), metav1.DeleteOptions{ + PropagationPolicy: &deletePolicy, + }) } diff --git a/internal/k8stest/k8s_telemetrygen.go b/internal/k8stest/k8s_telemetrygen.go index e9aed1a856e0..c620d499a333 100644 --- a/internal/k8stest/k8s_telemetrygen.go +++ b/internal/k8stest/k8s_telemetrygen.go @@ -16,7 +16,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" ) type TelemetrygenObjInfo struct { @@ -26,26 +25,33 @@ type TelemetrygenObjInfo struct { Workload string } -func CreateTelemetryGenObjects(t *testing.T, client *dynamic.DynamicClient, testID string) ([]*unstructured.Unstructured, []*TelemetrygenObjInfo) { +type TelemetrygenCreateOpts struct { + TestID string + ManifestsDir string + OtlpEndpoint string + DataTypes []string +} + +func CreateTelemetryGenObjects(t *testing.T, client *K8sClient, createOpts *TelemetrygenCreateOpts) ([]*unstructured.Unstructured, []*TelemetrygenObjInfo) { telemetrygenObjInfos := make([]*TelemetrygenObjInfo, 0) - manifestsDir := filepath.Join(".", "testdata", "e2e", "telemetrygen") - manifestFiles, err := os.ReadDir(manifestsDir) - require.NoErrorf(t, err, "failed to read telemetrygen manifests directory %s", manifestsDir) + manifestFiles, err := os.ReadDir(createOpts.ManifestsDir) + require.NoErrorf(t, err, "failed to read telemetrygen manifests directory %s", createOpts.ManifestsDir) createdObjs := make([]*unstructured.Unstructured, 0, len(manifestFiles)) for _, manifestFile := range manifestFiles { - tmpl := template.Must(template.New(manifestFile.Name()).ParseFiles(filepath.Join(manifestsDir, manifestFile.Name()))) - for _, dataType := range []string{"metrics", "logs", "traces"} { + tmpl := template.Must(template.New(manifestFile.Name()).ParseFiles(filepath.Join(createOpts.ManifestsDir, manifestFile.Name()))) + for _, dataType := range createOpts.DataTypes { manifest := &bytes.Buffer{} require.NoError(t, tmpl.Execute(manifest, map[string]string{ - "Name": "telemetrygen-" + testID, + "Name": "telemetrygen-" + createOpts.TestID, "DataType": dataType, - "OTLPEndpoint": "otelcol-" + testID + ":4317", + "OTLPEndpoint": createOpts.OtlpEndpoint, + "TestID": createOpts.TestID, })) obj, err := CreateObject(client, manifest.Bytes()) require.NoErrorf(t, err, "failed to create telemetrygen object from manifest %s", manifestFile.Name()) selector := obj.Object["spec"].(map[string]any)["selector"] telemetrygenObjInfos = append(telemetrygenObjInfos, &TelemetrygenObjInfo{ - Namespace: "default", + Namespace: obj.GetNamespace(), PodLabelSelectors: selector.(map[string]any)["matchLabels"].(map[string]any), DataType: dataType, Workload: obj.GetKind(), @@ -56,13 +62,13 @@ func CreateTelemetryGenObjects(t *testing.T, client *dynamic.DynamicClient, test return createdObjs, telemetrygenObjInfos } -func WaitForTelemetryGenToStart(t *testing.T, client *dynamic.DynamicClient, podNamespace string, podLabels map[string]any, workload, dataType string) { +func WaitForTelemetryGenToStart(t *testing.T, client *K8sClient, podNamespace string, podLabels map[string]any, workload, dataType string) { podGVR := schema.GroupVersionResource{Version: "v1", Resource: "pods"} listOptions := metav1.ListOptions{LabelSelector: SelectorFromMap(podLabels).String()} podTimeoutMinutes := 3 var podPhase string require.Eventually(t, func() bool { - list, err := client.Resource(podGVR).Namespace(podNamespace).List(context.Background(), listOptions) + list, err := client.DynamicClient.Resource(podGVR).Namespace(podNamespace).List(context.Background(), listOptions) require.NoError(t, err, "failed to list collector pods") if len(list.Items) == 0 { return false diff --git a/processor/k8sattributesprocessor/README.md b/processor/k8sattributesprocessor/README.md index edce0ed2461e..a2c21b8f9d90 100644 --- a/processor/k8sattributesprocessor/README.md +++ b/processor/k8sattributesprocessor/README.md @@ -159,7 +159,9 @@ k8sattributes/2: ## Role-based access control -The k8sattributesprocessor needs `get`, `watch` and `list` permissions on both `pods` and `namespaces` resources, for all namespaces and pods included in the configured filters. Additionally, when using `k8s.deployment.uid` or `k8s.deployment.name` the processor also needs `get`, `watch` and `list` permissions for `replicasets` resources. When extracting metadatas from `node`, the processor needs `get`, `watch` and `list` permissions for `nodes` resources. +## Cluster-scoped RBAC + +If you'd like to set up the k8sattributesprocessor to receive telemetry from across namespaces, it will need `get`, `watch` and `list` permissions on both `pods` and `namespaces` resources, for all namespaces and pods included in the configured filters. Additionally, when using `k8s.deployment.uid` or `k8s.deployment.name` the processor also needs `get`, `watch` and `list` permissions for `replicasets` resources. When extracting metadatas from `node`, the processor needs `get`, `watch` and `list` permissions for `nodes` resources. Here is an example of a `ClusterRole` to give a `ServiceAccount` the necessary permissions for all pods, nodes, and namespaces in the cluster (replace `` with a namespace where collector is deployed): @@ -199,6 +201,51 @@ roleRef: apiGroup: rbac.authorization.k8s.io ``` +### Namespace-scoped RBAC +When running the k8sattributesprocessor to receive telemetry traffic from pods in a specific namespace, you can use a k8s `Role` and `Rolebinding` to provide collector access to query pods and replicasets in the namespace. This would require setting the `filter::namespace` config as shown below. +```yaml +k8sattributes: + filter: + namespace: +``` +With the namespace filter set, the processor will only look up pods and replicasets in the selected namespace. Note that with just a role binding, the processor can not query metadata such as labels and annotations from k8s `nodes` and `namespaces` which are cluster-scoped objects. This also means that the processor can not set the value for `k8s.cluster.uid` attribute if enabled, since the `k8s.cluster.uid` attribute is set to the uid of the namespace `kube-system` which is not queryable with namespaced rbac. + +Example `Role` and `RoleBinding` to create in the namespace being watched. +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: otel-collector + namespace: +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: otel-collector + namespace: +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "watch", "list"] +- apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: otel-collector + namespace: +subjects: +- kind: ServiceAccount + name: otel-collector + namespace: +roleRef: + kind: Role + name: otel-collector + apiGroup: rbac.authorization.k8s.io +``` + ## Deployment scenarios The processor can be used in collectors deployed both as an agent (Kubernetes DaemonSet) or as a gateway (Kubernetes Deployment). diff --git a/processor/k8sattributesprocessor/e2e_test.go b/processor/k8sattributesprocessor/e2e_test.go index b80e9e38bf74..9057ce96c06f 100644 --- a/processor/k8sattributesprocessor/e2e_test.go +++ b/processor/k8sattributesprocessor/e2e_test.go @@ -8,6 +8,8 @@ package k8sattributesprocessor import ( "context" "fmt" + "os" + "path/filepath" "regexp" "testing" "time" @@ -22,8 +24,6 @@ import ( "go.opentelemetry.io/collector/receiver/otlpreceiver" "go.opentelemetry.io/collector/receiver/receivertest" "go.uber.org/multierr" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/tools/clientcmd" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8stest" ) @@ -32,10 +32,11 @@ const ( equal = iota regex exist + testKubeConfig = "/tmp/kube-config-otelcol-e2e-testing" + uidRe = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}" + startTimeRe = "^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}%3A\\\\d{2}%3A\\\\d{2}(?:%2E\\\\d+)?[A-Z]?(?:[+.-](?:08%3A\\\\d{2}|\\\\d{2}[A-Z]))?$" ) -const testKubeConfig = "/tmp/kube-config-otelcol-e2e-testing" - type expectedValue struct { mode int value string @@ -48,20 +49,32 @@ func newExpectedValue(mode int, value string) *expectedValue { } } -// TestE2E tests the k8s attributes processor with a real k8s cluster. +// TestE2E_ClusterRBAC tests the k8s attributes processor in a k8s cluster with the collector's service account having +// cluster-wide permissions to list/watch namespaces, nodes, pods and replicasets. The config in the test does not +// set filter::namespace. // The test requires a prebuilt otelcontribcol image uploaded to a kind k8s cluster defined in // `/tmp/kube-config-otelcol-e2e-testing`. Run the following command prior to running the test locally: // // kind create cluster --kubeconfig=/tmp/kube-config-otelcol-e2e-testing // make docker-otelcontribcol // KUBECONFIG=/tmp/kube-config-otelcol-e2e-testing kind load docker-image otelcontribcol:latest -func TestE2E(t *testing.T) { +func TestE2E_ClusterRBAC(t *testing.T) { + testDir := filepath.Join("testdata", "e2e", "clusterrbac") - kubeConfig, err := clientcmd.BuildConfigFromFlags("", testKubeConfig) - require.NoError(t, err) - dynamicClient, err := dynamic.NewForConfig(kubeConfig) + k8sClient, err := k8stest.NewK8sClient(testKubeConfig) require.NoError(t, err) + nsFile := filepath.Join(testDir, "namespace.yaml") + buf, err := os.ReadFile(nsFile) + require.NoErrorf(t, err, "failed to read namespace object file %s", nsFile) + nsObj, err := k8stest.CreateObject(k8sClient, buf) + require.NoErrorf(t, err, "failed to create k8s namespace from file %s", nsFile) + + testNs := nsObj.GetName() + defer func() { + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, nsObj), "failed to delete namespace %s", testNs) + }() + metricsConsumer := new(consumertest.MetricsSink) tracesConsumer := new(consumertest.TracesSink) logsConsumer := new(consumertest.LogsSink) @@ -69,16 +82,22 @@ func TestE2E(t *testing.T) { defer shutdownSinks() testID := uuid.NewString()[:8] - collectorObjs := k8stest.CreateCollectorObjects(t, dynamicClient, testID) - telemetryGenObjs, telemetryGenObjInfos := k8stest.CreateTelemetryGenObjects(t, dynamicClient, testID) + collectorObjs := k8stest.CreateCollectorObjects(t, k8sClient, testID, filepath.Join(testDir, "collector")) + createTeleOpts := &k8stest.TelemetrygenCreateOpts{ + ManifestsDir: filepath.Join(testDir, "telemetrygen"), + TestID: testID, + OtlpEndpoint: fmt.Sprintf("otelcol-%s.%s:4317", testID, testNs), + DataTypes: []string{"metrics", "logs", "traces"}, + } + telemetryGenObjs, telemetryGenObjInfos := k8stest.CreateTelemetryGenObjects(t, k8sClient, createTeleOpts) defer func() { for _, obj := range append(collectorObjs, telemetryGenObjs...) { - require.NoErrorf(t, k8stest.DeleteObject(dynamicClient, obj), "failed to delete object %s", obj.GetName()) + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, obj), "failed to delete object %s", obj.GetName()) } }() for _, info := range telemetryGenObjInfos { - k8stest.WaitForTelemetryGenToStart(t, dynamicClient, info.Namespace, info.PodLabelSelectors, info.Workload, info.DataType) + k8stest.WaitForTelemetryGenToStart(t, k8sClient, info.Namespace, info.PodLabelSelectors, info.Workload, info.DataType) } wantEntries := 128 // Minimal number of metrics/traces/logs to wait for. @@ -95,22 +114,23 @@ func TestE2E(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-job", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-job-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-job"), - "k8s.job.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "job"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-job"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-job-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-job"), + "k8s.job.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "job"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-job"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -118,22 +138,23 @@ func TestE2E(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-statefulset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset-0"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset"), - "k8s.statefulset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset-0"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset"), + "k8s.statefulset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-statefulset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -141,23 +162,24 @@ func TestE2E(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.deployment.uid": newExpectedValue(exist, ""), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.deployment.uid": newExpectedValue(exist, ""), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -165,22 +187,23 @@ func TestE2E(t *testing.T) { dataType: component.DataTypeTraces, service: "test-traces-daemonset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-daemonset-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-daemonset"), - "k8s.daemonset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-daemonset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-daemonset-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-daemonset"), + "k8s.daemonset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-daemonset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -188,22 +211,23 @@ func TestE2E(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-job", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-job-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-job"), - "k8s.job.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "job"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-job"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-job-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-job"), + "k8s.job.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "job"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-job"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -211,22 +235,23 @@ func TestE2E(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-statefulset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset-0"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset"), - "k8s.statefulset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset-0"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset"), + "k8s.statefulset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-statefulset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -234,23 +259,24 @@ func TestE2E(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.deployment.uid": newExpectedValue(exist, ""), - "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "deployment"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.deployment.uid": newExpectedValue(exist, ""), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -258,22 +284,23 @@ func TestE2E(t *testing.T) { dataType: component.DataTypeMetrics, service: "test-metrics-daemonset", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-daemonset-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-daemonset"), - "k8s.daemonset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-daemonset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-daemonset-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-daemonset"), + "k8s.daemonset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-daemonset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), }, }, { @@ -281,41 +308,208 @@ func TestE2E(t *testing.T) { dataType: component.DataTypeLogs, service: "test-logs-job", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-job-[a-z0-9]*"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-job-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-job"), + "k8s.job.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "job"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-job"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + }, + }, + { + name: "logs-statefulset", + dataType: component.DataTypeLogs, + service: "test-logs-statefulset", + attrs: map[string]*expectedValue{ + "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset-0"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset"), + "k8s.statefulset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + }, + }, + { + name: "logs-deployment", + dataType: component.DataTypeLogs, + service: "test-logs-deployment", + attrs: map[string]*expectedValue{ + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.deployment.uid": newExpectedValue(exist, ""), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + }, + }, + { + name: "logs-daemonset", + dataType: component.DataTypeLogs, + service: "test-logs-daemonset", + attrs: map[string]*expectedValue{ + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-daemonset-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, testNs), + "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-daemonset"), + "k8s.daemonset.uid": newExpectedValue(exist, ""), + "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-daemonset"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + }, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + switch tc.dataType { + case component.DataTypeTraces: + scanTracesForAttributes(t, tracesConsumer, tc.service, tc.attrs) + case component.DataTypeMetrics: + scanMetricsForAttributes(t, metricsConsumer, tc.service, tc.attrs) + case component.DataTypeLogs: + scanLogsForAttributes(t, logsConsumer, tc.service, tc.attrs) + default: + t.Fatalf("unknown data type %s", tc.dataType) + } + }) + } +} + +// Test with `filter::namespace` set and only role binding to collector's SA. We can't get node and namespace labels/annotations. +func TestE2E_NamespacedRBAC(t *testing.T) { + testDir := filepath.Join("testdata", "e2e", "namespacedrbac") + + k8sClient, err := k8stest.NewK8sClient(testKubeConfig) + require.NoError(t, err) + + nsFile := filepath.Join(testDir, "namespace.yaml") + buf, err := os.ReadFile(nsFile) + require.NoErrorf(t, err, "failed to read namespace object file %s", nsFile) + nsObj, err := k8stest.CreateObject(k8sClient, buf) + require.NoErrorf(t, err, "failed to create k8s namespace from file %s", nsFile) + nsName := nsObj.GetName() + defer func() { + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, nsObj), "failed to delete namespace %s", nsName) + }() + + metricsConsumer := new(consumertest.MetricsSink) + tracesConsumer := new(consumertest.TracesSink) + logsConsumer := new(consumertest.LogsSink) + shutdownSinks := startUpSinks(t, metricsConsumer, tracesConsumer, logsConsumer) + defer shutdownSinks() + + testID := uuid.NewString()[:8] + collectorObjs := k8stest.CreateCollectorObjects(t, k8sClient, testID, filepath.Join(testDir, "collector")) + createTeleOpts := &k8stest.TelemetrygenCreateOpts{ + ManifestsDir: filepath.Join(testDir, "telemetrygen"), + TestID: testID, + OtlpEndpoint: fmt.Sprintf("otelcol-%s.%s:4317", testID, nsName), + DataTypes: []string{"metrics", "logs", "traces"}, + } + telemetryGenObjs, telemetryGenObjInfos := k8stest.CreateTelemetryGenObjects(t, k8sClient, createTeleOpts) + defer func() { + for _, obj := range append(collectorObjs, telemetryGenObjs...) { + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, obj), "failed to delete object %s", obj.GetName()) + } + }() + + for _, info := range telemetryGenObjInfos { + k8stest.WaitForTelemetryGenToStart(t, k8sClient, info.Namespace, info.PodLabelSelectors, info.Workload, info.DataType) + } + + wantEntries := 20 // Minimal number of metrics/traces/logs to wait for. + waitForData(t, wantEntries, metricsConsumer, tracesConsumer, logsConsumer) + + tcs := []struct { + name string + dataType component.DataType + service string + attrs map[string]*expectedValue + }{ + { + name: "traces-deployment", + dataType: component.DataTypeTraces, + service: "test-traces-deployment", + attrs: map[string]*expectedValue{ + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.job.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-job"), - "k8s.job.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "job"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-job"), + "k8s.namespace.name": newExpectedValue(equal, nsName), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), "container.image.tag": newExpectedValue(equal, "latest"), "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), }, }, { - name: "logs-statefulset", - dataType: component.DataTypeLogs, - service: "test-logs-statefulset", + name: "metrics-deployment", + dataType: component.DataTypeMetrics, + service: "test-metrics-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset-0"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.statefulset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset"), - "k8s.statefulset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "statefulset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-statefulset"), + "k8s.namespace.name": newExpectedValue(equal, nsName), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), "container.image.tag": newExpectedValue(equal, "latest"), "container.id": newExpectedValue(exist, ""), @@ -328,45 +522,183 @@ func TestE2E(t *testing.T) { attrs: map[string]*expectedValue{ "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, startTimeRe), "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), + "k8s.namespace.name": newExpectedValue(equal, nsName), "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), - "k8s.deployment.uid": newExpectedValue(exist, ""), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), - "k8s.replicaset.uid": newExpectedValue(exist, ""), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), "k8s.annotations.workload": newExpectedValue(equal, "deployment"), "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), "container.image.tag": newExpectedValue(equal, "latest"), "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), }, }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + switch tc.dataType { + case component.DataTypeTraces: + scanTracesForAttributes(t, tracesConsumer, tc.service, tc.attrs) + case component.DataTypeMetrics: + scanMetricsForAttributes(t, metricsConsumer, tc.service, tc.attrs) + case component.DataTypeLogs: + scanLogsForAttributes(t, logsConsumer, tc.service, tc.attrs) + default: + t.Fatalf("unknown data type %s", tc.dataType) + } + }) + } +} + +// Test with `filter::namespace` set, role binding for namespace-scoped objects (pod, replicaset) and clusterrole +// binding for node and namespace objects. +func TestE2E_MixRBAC(t *testing.T) { + testDir := filepath.Join("testdata", "e2e", "mixrbac") + + k8sClient, err := k8stest.NewK8sClient(testKubeConfig) + require.NoError(t, err) + + metricsConsumer := new(consumertest.MetricsSink) + tracesConsumer := new(consumertest.TracesSink) + logsConsumer := new(consumertest.LogsSink) + shutdownSinks := startUpSinks(t, metricsConsumer, tracesConsumer, logsConsumer) + defer shutdownSinks() + + var workloadNs, otelNs string + testID := uuid.NewString()[:8] + for i, nsManifest := range []string{filepath.Join(testDir, "otelcol-namespace.yaml"), filepath.Join(testDir, "workload-namespace.yaml")} { + buf, err := os.ReadFile(nsManifest) + require.NoErrorf(t, err, "failed to read namespace object file %s", nsManifest) + nsObj, err := k8stest.CreateObject(k8sClient, buf) + require.NoErrorf(t, err, "failed to create k8s namespace from file %s", nsManifest) + switch i { + case 0: + otelNs = nsObj.GetName() + case 1: + workloadNs = nsObj.GetName() + } + + defer func() { + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, nsObj), "failed to delete namespace %s", nsObj.GetName()) + }() + } + + collectorObjs := k8stest.CreateCollectorObjects(t, k8sClient, testID, filepath.Join(testDir, "collector")) + defer func() { + for _, obj := range collectorObjs { + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, obj), "failed to delete object %s", obj.GetName()) + } + }() + + createTeleOpts := &k8stest.TelemetrygenCreateOpts{ + ManifestsDir: filepath.Join(testDir, "telemetrygen"), + TestID: testID, + OtlpEndpoint: fmt.Sprintf("otelcol-%s.%s:4317", testID, otelNs), + DataTypes: []string{"metrics", "logs", "traces"}, + } + + telemetryGenObjs, telemetryGenObjInfos := k8stest.CreateTelemetryGenObjects(t, k8sClient, createTeleOpts) + defer func() { + for _, obj := range telemetryGenObjs { + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, obj), "failed to delete object %s", obj.GetName()) + } + }() + + for _, info := range telemetryGenObjInfos { + k8stest.WaitForTelemetryGenToStart(t, k8sClient, info.Namespace, info.PodLabelSelectors, info.Workload, info.DataType) + } + + wantEntries := 20 // Minimal number of metrics/traces/logs to wait for. + waitForData(t, wantEntries, metricsConsumer, tracesConsumer, logsConsumer) + + tcs := []struct { + name string + dataType component.DataType + service string + attrs map[string]*expectedValue + }{ { - name: "logs-daemonset", + name: "traces-deployment", + dataType: component.DataTypeTraces, + service: "test-traces-deployment", + attrs: map[string]*expectedValue{ + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, workloadNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-traces-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-traces-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + }, + }, + { + name: "metrics-deployment", + dataType: component.DataTypeMetrics, + service: "test-metrics-deployment", + attrs: map[string]*expectedValue{ + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, workloadNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-metrics-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-metrics-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), + }, + }, + { + name: "logs-deployment", dataType: component.DataTypeLogs, - service: "test-logs-daemonset", + service: "test-logs-deployment", attrs: map[string]*expectedValue{ - "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-daemonset-[a-z0-9]*"), - "k8s.pod.ip": newExpectedValue(exist, ""), - "k8s.pod.uid": newExpectedValue(exist, ""), - "k8s.pod.start_time": newExpectedValue(exist, ""), - "k8s.node.name": newExpectedValue(exist, ""), - "k8s.namespace.name": newExpectedValue(equal, "default"), - "k8s.daemonset.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-daemonset"), - "k8s.daemonset.uid": newExpectedValue(exist, ""), - "k8s.annotations.workload": newExpectedValue(equal, "daemonset"), - "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-daemonset"), - "k8s.container.name": newExpectedValue(equal, "telemetrygen"), - "k8s.cluster.uid": newExpectedValue(exist, ""), - "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), - "container.image.tag": newExpectedValue(equal, "latest"), - "container.id": newExpectedValue(exist, ""), - "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.pod.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*-[a-z0-9]*"), + "k8s.pod.ip": newExpectedValue(exist, ""), + "k8s.pod.uid": newExpectedValue(regex, uidRe), + "k8s.pod.start_time": newExpectedValue(exist, ""), + "k8s.node.name": newExpectedValue(exist, ""), + "k8s.namespace.name": newExpectedValue(equal, workloadNs), + "k8s.deployment.name": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.deployment.uid": newExpectedValue(regex, uidRe), + "k8s.replicaset.name": newExpectedValue(regex, "telemetrygen-"+testID+"-logs-deployment-[a-z0-9]*"), + "k8s.replicaset.uid": newExpectedValue(regex, uidRe), + "k8s.annotations.workload": newExpectedValue(equal, "deployment"), + "k8s.labels.app": newExpectedValue(equal, "telemetrygen-"+testID+"-logs-deployment"), + "k8s.container.name": newExpectedValue(equal, "telemetrygen"), + "container.image.name": newExpectedValue(equal, "ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen"), + "container.image.tag": newExpectedValue(equal, "latest"), + "container.id": newExpectedValue(exist, ""), + "k8s.namespace.labels.foons": newExpectedValue(equal, "barns"), + "k8s.node.labels.foo": newExpectedValue(equal, "too"), + "k8s.cluster.uid": newExpectedValue(regex, uidRe), }, }, } diff --git a/processor/k8sattributesprocessor/internal/kube/client.go b/processor/k8sattributesprocessor/internal/kube/client.go index 805820d3ad4a..6364d46a4e4e 100644 --- a/processor/k8sattributesprocessor/internal/kube/client.go +++ b/processor/k8sattributesprocessor/internal/kube/client.go @@ -120,14 +120,17 @@ func New(logger *zap.Logger, apiCfg k8sconfig.APIConfig, rules ExtractionRules, } if newNamespaceInformer == nil { - // if rules to extract metadata from namespace is configured use namespace shared informer containing - // all namespaces including kube-system which contains cluster uid information (kube-system-uid) - if c.extractNamespaceLabelsAnnotations() { + switch { + case c.extractNamespaceLabelsAnnotations(): + // if rules to extract metadata from namespace is configured use namespace shared informer containing + // all namespaces including kube-system which contains cluster uid information (kube-system-uid) newNamespaceInformer = newNamespaceSharedInformer - } else { + case rules.ClusterUID: // use kube-system shared informer to only watch kube-system namespace // reducing overhead of watching all the namespaces newNamespaceInformer = newKubeSystemSharedInformer + default: + newNamespaceInformer = NewNoOpInformer } } diff --git a/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/clusterrole.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/clusterrole.yaml new file mode 100644 index 000000000000..e93e29729996 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/clusterrole.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Name }} +rules: + - apiGroups: [""] + resources: ["pods", "nodes"] + verbs: ["get", "watch", "list"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "watch", "list"] + - apiGroups: [ "" ] + resources: ["namespaces"] + verbs: [ "get", "watch", "list" ] diff --git a/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/clusterrolebinding.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/clusterrolebinding.yaml new file mode 100644 index 000000000000..41b16aef0ead --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/clusterrolebinding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Name }} +subjects: + - kind: ServiceAccount + name: {{ .Name }} + namespace: e2ek8sattribute-clusterrbac diff --git a/processor/k8sattributesprocessor/testdata/e2e/collector/configmap.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/configmap.yaml similarity index 95% rename from processor/k8sattributesprocessor/testdata/e2e/collector/configmap.yaml rename to processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/configmap.yaml index e82cead3fdec..384903a4b1fd 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/collector/configmap.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/configmap.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: ConfigMap metadata: name: {{ .Name }}-config - namespace: default + namespace: e2ek8sattribute-clusterrbac data: relay: | exporters: @@ -25,6 +25,8 @@ data: tag_name: k8s.labels.app - from: node key: foo + - from: namespace + key: foons metadata: - k8s.pod.name - k8s.pod.start_time diff --git a/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/deployment.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/deployment.yaml new file mode 100644 index 000000000000..a2011605a3e0 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/deployment.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Name }} + namespace: e2ek8sattribute-clusterrbac +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: opentelemetry-collector + app.kubernetes.io/instance: {{ .Name }} + template: + metadata: + labels: + app.kubernetes.io/name: opentelemetry-collector + app.kubernetes.io/instance: {{ .Name }} + spec: + serviceAccountName: {{ .Name }} + containers: + - name: opentelemetry-collector + command: + - /otelcontribcol + - --config=/conf/relay.yaml + image: "otelcontribcol:latest" + imagePullPolicy: Never + ports: + - name: otlp + containerPort: 4317 + protocol: TCP + env: + - name: MY_POD_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP + livenessProbe: + httpGet: + path: / + port: 13133 + initialDelaySeconds: 3 + readinessProbe: + httpGet: + path: / + port: 13133 + initialDelaySeconds: 3 + resources: + limits: + cpu: 128m + memory: 256Mi + volumeMounts: + - mountPath: /conf + name: opentelemetry-collector-configmap + volumes: + - name: opentelemetry-collector-configmap + configMap: + name: {{ .Name }}-config + items: + - key: relay + path: relay.yaml diff --git a/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/service.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/service.yaml new file mode 100644 index 000000000000..2f4ae9540089 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Name }} + namespace: e2ek8sattribute-clusterrbac +spec: + type: ClusterIP + ports: + - name: otlp + port: 4317 + targetPort: 4317 + protocol: TCP + appProtocol: grpc + selector: + app.kubernetes.io/name: opentelemetry-collector + app.kubernetes.io/instance: {{ .Name }} diff --git a/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/serviceaccount.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/serviceaccount.yaml new file mode 100644 index 000000000000..21cde70461ce --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/collector/serviceaccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Name }} + namespace: e2ek8sattribute-clusterrbac diff --git a/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/namespace.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/namespace.yaml new file mode 100644 index 000000000000..c5161a2e8dc3 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: e2ek8sattribute-clusterrbac + labels: + foons: barns diff --git a/processor/k8sattributesprocessor/testdata/e2e/telemetrygen/daemonset.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/daemonset.yaml similarity index 95% rename from processor/k8sattributesprocessor/testdata/e2e/telemetrygen/daemonset.yaml rename to processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/daemonset.yaml index 9d8effb27124..4ac9fecf815e 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/telemetrygen/daemonset.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/daemonset.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: DaemonSet metadata: name: {{ .Name }}-{{ .DataType }}-daemonset - namespace: default + namespace: e2ek8sattribute-clusterrbac spec: selector: matchLabels: diff --git a/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/deployment.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/deployment.yaml new file mode 100644 index 000000000000..8344d9b8ad90 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Name }}-{{ .DataType }}-deployment + namespace: e2ek8sattribute-clusterrbac +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Name }}-{{ .DataType }}-deployment + template: + metadata: + annotations: + workload: deployment + labels: + app: {{ .Name }}-{{ .DataType }}-deployment + spec: + containers: + - command: + - /telemetrygen + - {{ .DataType }} + - --otlp-insecure + - --otlp-endpoint={{ .OTLPEndpoint }} + - --duration=36000s + - --rate=1 + - --otlp-attributes=service.name="test-{{ .DataType }}-deployment" + - --otlp-attributes=k8s.container.name="telemetrygen" +{{- if eq .DataType "traces" }} + - --status-code= +{{- end }} + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest + imagePullPolicy: IfNotPresent + name: telemetrygen + restartPolicy: Always diff --git a/processor/k8sattributesprocessor/testdata/e2e/telemetrygen/job.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/job.yaml similarity index 94% rename from processor/k8sattributesprocessor/testdata/e2e/telemetrygen/job.yaml rename to processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/job.yaml index dd0e9dcdf345..76a003ea9533 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/telemetrygen/job.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/job.yaml @@ -2,7 +2,7 @@ apiVersion: batch/v1 kind: Job metadata: name: {{ .Name }}-{{ .DataType}}-job - namespace: default + namespace: e2ek8sattribute-clusterrbac spec: template: metadata: diff --git a/processor/k8sattributesprocessor/testdata/e2e/telemetrygen/statefulset.yaml b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/statefulset.yaml similarity index 95% rename from processor/k8sattributesprocessor/testdata/e2e/telemetrygen/statefulset.yaml rename to processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/statefulset.yaml index 6dcb9a2739ae..4ef983a8a3ef 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/telemetrygen/statefulset.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/clusterrbac/telemetrygen/statefulset.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: StatefulSet metadata: name: {{ .Name }}-{{ .DataType }}-statefulset - namespace: default + namespace: e2ek8sattribute-clusterrbac spec: serviceName: {{ .Name }}-{{ .DataType }}-statefulset replicas: 1 diff --git a/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/clusterrole.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/clusterrole.yaml new file mode 100644 index 000000000000..536113c9c1a8 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/clusterrole.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Name }} +rules: + - apiGroups: [ "" ] + resources: ["namespaces"] + verbs: [ "list", "get", "watch" ] + - apiGroups: [ "" ] + resources: ["nodes"] + verbs: [ "get", "watch", "list" ] diff --git a/processor/k8sattributesprocessor/testdata/e2e/collector/clusterrolebinding.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/clusterrolebinding.yaml similarity index 86% rename from processor/k8sattributesprocessor/testdata/e2e/collector/clusterrolebinding.yaml rename to processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/clusterrolebinding.yaml index b792cc6309de..1f06e35fc54e 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/collector/clusterrolebinding.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/clusterrolebinding.yaml @@ -9,4 +9,4 @@ roleRef: subjects: - kind: ServiceAccount name: {{ .Name }} - namespace: default + namespace: e2ek8sattribute-mixrbac diff --git a/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/configmap.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/configmap.yaml new file mode 100644 index 000000000000..33a368cf7bec --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/configmap.yaml @@ -0,0 +1,78 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Name }}-config + namespace: e2ek8sattribute-mixrbac +data: + relay: | + exporters: + otlp: + endpoint: {{ .HostEndpoint }}:4317 + tls: + insecure: true + extensions: + health_check: {} + processors: + k8sattributes: + filter: + namespace: e2ek8sattribute-workloadns + extract: + annotations: + - from: pod + key: workload + tag_name: k8s.annotations.workload + labels: + - from: pod + key: app + tag_name: k8s.labels.app + - from: node + key: foo + - from: namespace + key: foons + metadata: + - k8s.pod.name + - k8s.pod.start_time + - k8s.pod.uid + - k8s.namespace.name + - k8s.deployment.name + - k8s.deployment.uid + - k8s.replicaset.name + - k8s.replicaset.uid + - k8s.node.name + - container.id + - container.image.name + - container.image.tag + - k8s.cluster.uid + pod_association: + - sources: + - from: connection + receivers: + otlp: + protocols: + grpc: + endpoint: ${env:MY_POD_IP}:4317 + service: + extensions: + - health_check + pipelines: + metrics: + exporters: + - otlp + processors: + - k8sattributes + receivers: + - otlp + traces: + exporters: + - otlp + processors: + - k8sattributes + receivers: + - otlp + logs: + exporters: + - otlp + processors: + - k8sattributes + receivers: + - otlp diff --git a/processor/k8sattributesprocessor/testdata/e2e/collector/deployment.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/deployment.yaml similarity index 97% rename from processor/k8sattributesprocessor/testdata/e2e/collector/deployment.yaml rename to processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/deployment.yaml index 841c472b04f4..14860e9513fd 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/collector/deployment.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Name }} - namespace: default + namespace: e2ek8sattribute-mixrbac spec: replicas: 1 selector: diff --git a/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/role-workloadns.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/role-workloadns.yaml new file mode 100644 index 000000000000..70a16a2de0d5 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/role-workloadns.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: otelcol-workloadns + namespace: e2ek8sattribute-workloadns +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "watch", "list"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "watch", "list"] diff --git a/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/rolebinding-workloadns.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/rolebinding-workloadns.yaml new file mode 100644 index 000000000000..3ece0171afdd --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/rolebinding-workloadns.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: otelcol-workloadns + namespace: e2ek8sattribute-workloadns +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: otelcol-workloadns +subjects: + - kind: ServiceAccount + name: otelcol-{{ .TestID }} + namespace: e2ek8sattribute-mixrbac diff --git a/processor/k8sattributesprocessor/testdata/e2e/collector/service.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/service.yaml similarity index 89% rename from processor/k8sattributesprocessor/testdata/e2e/collector/service.yaml rename to processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/service.yaml index 1bbfffb99197..fe014682fb8a 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/collector/service.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: name: {{ .Name }} - namespace: default + namespace: e2ek8sattribute-mixrbac spec: type: ClusterIP ports: diff --git a/processor/k8sattributesprocessor/testdata/e2e/collector/serviceaccount.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/serviceaccount.yaml similarity index 64% rename from processor/k8sattributesprocessor/testdata/e2e/collector/serviceaccount.yaml rename to processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/serviceaccount.yaml index bdb3a8dd1b8f..e688469a9e49 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/collector/serviceaccount.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/collector/serviceaccount.yaml @@ -2,4 +2,4 @@ apiVersion: v1 kind: ServiceAccount metadata: name: {{ .Name }} - namespace: default + namespace: e2ek8sattribute-mixrbac diff --git a/processor/k8sattributesprocessor/testdata/e2e/mixrbac/otelcol-namespace.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/otelcol-namespace.yaml new file mode 100644 index 000000000000..a43f6cae18d9 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/otelcol-namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: e2ek8sattribute-mixrbac + labels: + foons: barns diff --git a/processor/k8sattributesprocessor/testdata/e2e/telemetrygen/deployment.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/telemetrygen/deployment.yaml similarity index 95% rename from processor/k8sattributesprocessor/testdata/e2e/telemetrygen/deployment.yaml rename to processor/k8sattributesprocessor/testdata/e2e/mixrbac/telemetrygen/deployment.yaml index bc31edac33ed..a09257347f5e 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/telemetrygen/deployment.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/telemetrygen/deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: {{ .Name }}-{{ .DataType }}-deployment - namespace: default + namespace: e2ek8sattribute-workloadns spec: replicas: 1 selector: diff --git a/processor/k8sattributesprocessor/testdata/e2e/mixrbac/workload-namespace.yaml b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/workload-namespace.yaml new file mode 100644 index 000000000000..4cf0a4a67531 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/mixrbac/workload-namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: e2ek8sattribute-workloadns + labels: + foons: barns diff --git a/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/configmap.yaml b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/configmap.yaml new file mode 100644 index 000000000000..e8170c327bf5 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/configmap.yaml @@ -0,0 +1,82 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Name }}-config + namespace: e2ek8sattribute-namespacedrbac +data: + relay: | + exporters: + otlp: + endpoint: {{ .HostEndpoint }}:4317 + tls: + insecure: true + extensions: + health_check: {} + processors: + k8sattributes: + filter: + namespace: e2ek8sattribute-namespacedrbac + node_from_env_var: MY_NODE_NAME + labels: + - key: component + value: telemetrygen + op: equals + fields: + - key: spec.restartPolicy + value: Never + op: not-equals + extract: + annotations: + - from: pod + key: workload + tag_name: k8s.annotations.workload + labels: + - from: pod + key: app + tag_name: k8s.labels.app + metadata: + - k8s.pod.name + - k8s.pod.start_time + - k8s.pod.uid + - k8s.namespace.name + - k8s.deployment.name + - k8s.deployment.uid + - k8s.replicaset.name + - k8s.replicaset.uid + - k8s.node.name + - container.id + - container.image.name + - container.image.tag + pod_association: + - sources: + - from: connection + receivers: + otlp: + protocols: + grpc: + endpoint: ${env:MY_POD_IP}:4317 + service: + extensions: + - health_check + pipelines: + metrics: + exporters: + - otlp + processors: + - k8sattributes + receivers: + - otlp + traces: + exporters: + - otlp + processors: + - k8sattributes + receivers: + - otlp + logs: + exporters: + - otlp + processors: + - k8sattributes + receivers: + - otlp diff --git a/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/deployment.yaml b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/deployment.yaml new file mode 100644 index 000000000000..c4c54829bf68 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/deployment.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Name }} + namespace: e2ek8sattribute-namespacedrbac +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: opentelemetry-collector + app.kubernetes.io/instance: {{ .Name }} + template: + metadata: + labels: + app.kubernetes.io/name: opentelemetry-collector + app.kubernetes.io/instance: {{ .Name }} + spec: + serviceAccountName: {{ .Name }} + containers: + - name: opentelemetry-collector + command: + - /otelcontribcol + - --config=/conf/relay.yaml + - --feature-gates=k8sattr.rfc3339 + image: "otelcontribcol:latest" + imagePullPolicy: Never + ports: + - name: otlp + containerPort: 4317 + protocol: TCP + env: + - name: MY_POD_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP + - name: MY_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + livenessProbe: + httpGet: + path: / + port: 13133 + initialDelaySeconds: 3 + readinessProbe: + httpGet: + path: / + port: 13133 + initialDelaySeconds: 3 + resources: + limits: + cpu: 128m + memory: 256Mi + volumeMounts: + - mountPath: /conf + name: opentelemetry-collector-configmap + volumes: + - name: opentelemetry-collector-configmap + configMap: + name: {{ .Name }}-config + items: + - key: relay + path: relay.yaml diff --git a/processor/k8sattributesprocessor/testdata/e2e/collector/clusterrole.yaml b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/role.yaml similarity index 74% rename from processor/k8sattributesprocessor/testdata/e2e/collector/clusterrole.yaml rename to processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/role.yaml index 5bc4bfb348ab..336174eda79b 100644 --- a/processor/k8sattributesprocessor/testdata/e2e/collector/clusterrole.yaml +++ b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/role.yaml @@ -1,10 +1,11 @@ apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: Role metadata: name: {{ .Name }} + namespace: e2ek8sattribute-namespacedrbac rules: - apiGroups: [""] - resources: ["pods", "namespaces", "nodes"] + resources: ["pods"] verbs: ["get", "watch", "list"] - apiGroups: ["apps"] resources: ["replicasets"] diff --git a/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/rolebinding.yaml b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/rolebinding.yaml new file mode 100644 index 000000000000..fd39c71386ea --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/rolebinding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ .Name }} + namespace: e2ek8sattribute-namespacedrbac +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ .Name }} +subjects: + - kind: ServiceAccount + name: {{ .Name }} + namespace: e2ek8sattribute-namespacedrbac diff --git a/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/service.yaml b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/service.yaml new file mode 100644 index 000000000000..9e83585a9058 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Name }} + namespace: e2ek8sattribute-namespacedrbac +spec: + type: ClusterIP + ports: + - name: otlp + port: 4317 + targetPort: 4317 + protocol: TCP + appProtocol: grpc + selector: + app.kubernetes.io/name: opentelemetry-collector + app.kubernetes.io/instance: {{ .Name }} diff --git a/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/serviceaccount.yaml b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/serviceaccount.yaml new file mode 100644 index 000000000000..8e707e5f3ff6 --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/collector/serviceaccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Name }} + namespace: e2ek8sattribute-namespacedrbac diff --git a/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/namespace.yaml b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/namespace.yaml new file mode 100644 index 000000000000..baaecf01583a --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: e2ek8sattribute-namespacedrbac + labels: + foons: barns diff --git a/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/telemetrygen/deployment.yaml b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/telemetrygen/deployment.yaml new file mode 100644 index 000000000000..fd56c39d4eff --- /dev/null +++ b/processor/k8sattributesprocessor/testdata/e2e/namespacedrbac/telemetrygen/deployment.yaml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Name }}-{{ .DataType }}-deployment + namespace: e2ek8sattribute-namespacedrbac +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Name }}-{{ .DataType }}-deployment + template: + metadata: + annotations: + workload: deployment + labels: + app: {{ .Name }}-{{ .DataType }}-deployment + component: telemetrygen + spec: + containers: + - command: + - /telemetrygen + - {{ .DataType }} + - --otlp-insecure + - --otlp-endpoint={{ .OTLPEndpoint }} + - --duration=36000s + - --rate=1 + - --otlp-attributes=service.name="test-{{ .DataType }}-deployment" + - --otlp-attributes=k8s.container.name="telemetrygen" +{{- if eq .DataType "traces" }} + - --status-code= +{{- end }} + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest + imagePullPolicy: IfNotPresent + name: telemetrygen + restartPolicy: Always diff --git a/receiver/k8sclusterreceiver/e2e_test.go b/receiver/k8sclusterreceiver/e2e_test.go index 6530976d3461..fbe6aac0cd42 100644 --- a/receiver/k8sclusterreceiver/e2e_test.go +++ b/receiver/k8sclusterreceiver/e2e_test.go @@ -20,8 +20,6 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver/otlpreceiver" "go.opentelemetry.io/collector/receiver/receivertest" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/tools/clientcmd" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8stest" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" @@ -43,9 +41,8 @@ func TestE2E(t *testing.T) { expectedFile := filepath.Join("testdata", "e2e", "expected.yaml") expected, err := golden.ReadMetrics(expectedFile) require.NoError(t, err) - kubeConfig, err := clientcmd.BuildConfigFromFlags("", testKubeConfig) - require.NoError(t, err) - dynamicClient, err := dynamic.NewForConfig(kubeConfig) + + k8sClient, err := k8stest.NewK8sClient(testKubeConfig) require.NoError(t, err) metricsConsumer := new(consumertest.MetricsSink) @@ -53,11 +50,11 @@ func TestE2E(t *testing.T) { defer shutdownSink() testID := uuid.NewString()[:8] - collectorObjs := k8stest.CreateCollectorObjects(t, dynamicClient, testID) + collectorObjs := k8stest.CreateCollectorObjects(t, k8sClient, testID, "") defer func() { for _, obj := range append(collectorObjs) { - require.NoErrorf(t, k8stest.DeleteObject(dynamicClient, obj), "failed to delete object %s", obj.GetName()) + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, obj), "failed to delete object %s", obj.GetName()) } }() diff --git a/receiver/k8sobjectsreceiver/e2e_test.go b/receiver/k8sobjectsreceiver/e2e_test.go index e27c34acb179..c5df5472f01b 100644 --- a/receiver/k8sobjectsreceiver/e2e_test.go +++ b/receiver/k8sobjectsreceiver/e2e_test.go @@ -21,8 +21,6 @@ import ( "go.opentelemetry.io/collector/receiver/otlpreceiver" "go.opentelemetry.io/collector/receiver/receivertest" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/tools/clientcmd" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8stest" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" @@ -43,9 +41,7 @@ const ( func TestE2E(t *testing.T) { - kubeConfig, err := clientcmd.BuildConfigFromFlags("", testKubeConfig) - require.NoError(t, err) - dynamicClient, err := dynamic.NewForConfig(kubeConfig) + k8sClient, err := k8stest.NewK8sClient(testKubeConfig) require.NoError(t, err) testID := uuid.NewString()[:8] @@ -62,11 +58,11 @@ func TestE2E(t *testing.T) { }() // startup collector in k8s cluster - collectorObjs := k8stest.CreateCollectorObjects(t, dynamicClient, testID) + collectorObjs := k8stest.CreateCollectorObjects(t, k8sClient, testID, "") defer func() { for _, obj := range collectorObjs { - require.NoErrorf(t, k8stest.DeleteObject(dynamicClient, obj), "failed to delete object %s", obj.GetName()) + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, obj), "failed to delete object %s", obj.GetName()) } }() @@ -125,18 +121,18 @@ func TestE2E(t *testing.T) { for _, fileName := range tc.objectFileNames { obj, err := os.ReadFile(filepath.Join(testObjectsDir, fileName)) require.NoErrorf(t, err, "failed to read object file %s", fileName) - newObj, err := k8stest.CreateObject(dynamicClient, obj) + newObj, err := k8stest.CreateObject(k8sClient, obj) require.NoErrorf(t, err, "failed to create k8s object from file %s", fileName) testObjs = append(testObjs, newObj) } if tc.objectAction == createAndDelete { for _, obj := range testObjs { - require.NoErrorf(t, k8stest.DeleteObject(dynamicClient, obj), "failed to delete object %s", obj.GetName()) + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, obj), "failed to delete object %s", obj.GetName()) } } else { defer func() { for _, obj := range testObjs { - require.NoErrorf(t, k8stest.DeleteObject(dynamicClient, obj), "failed to delete object %s", obj.GetName()) + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, obj), "failed to delete object %s", obj.GetName()) } }() } diff --git a/receiver/kubeletstatsreceiver/e2e_test.go b/receiver/kubeletstatsreceiver/e2e_test.go index f8de3d0948a2..9d8c314524e4 100644 --- a/receiver/kubeletstatsreceiver/e2e_test.go +++ b/receiver/kubeletstatsreceiver/e2e_test.go @@ -19,8 +19,6 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver/otlpreceiver" "go.opentelemetry.io/collector/receiver/receivertest" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/tools/clientcmd" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8stest" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" @@ -35,9 +33,8 @@ func TestE2E(t *testing.T) { expectedFile := filepath.Join("testdata", "e2e", "expected.yaml") expected, err := golden.ReadMetrics(expectedFile) require.NoError(t, err) - kubeConfig, err := clientcmd.BuildConfigFromFlags("", testKubeConfig) - require.NoError(t, err) - dynamicClient, err := dynamic.NewForConfig(kubeConfig) + + k8sClient, err := k8stest.NewK8sClient(testKubeConfig) require.NoError(t, err) metricsConsumer := new(consumertest.MetricsSink) @@ -45,11 +42,11 @@ func TestE2E(t *testing.T) { defer shutdownSink() testID := uuid.NewString()[:8] - collectorObjs := k8stest.CreateCollectorObjects(t, dynamicClient, testID) + collectorObjs := k8stest.CreateCollectorObjects(t, k8sClient, testID, "") defer func() { for _, obj := range append(collectorObjs) { - require.NoErrorf(t, k8stest.DeleteObject(dynamicClient, obj), "failed to delete object %s", obj.GetName()) + require.NoErrorf(t, k8stest.DeleteObject(k8sClient, obj), "failed to delete object %s", obj.GetName()) } }()