From cbe843a70b1862a2b8cdbefdd9e05db547732d0f Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Fri, 21 Feb 2025 08:55:04 -0800 Subject: [PATCH] add test for k8s events option (#1670) * add test for k8s events option * fix typo --- .github/workflows/functional_test_v2.yaml | 1 + functional_tests/common.go | 63 +++ functional_tests/istio_test.go | 57 +-- functional_tests/k8sevents_test.go | 311 +++++++++++++ .../expected_k8sevents.yaml | 408 ++++++++++++++++++ .../k8sevents_values.yaml.tmpl | 21 + .../testobjects/1-serviceaccount.yaml | 5 + .../testdata_k8sevents/testobjects/2-svc.yaml | 12 + .../testdata_k8sevents/testobjects/3-sts.yaml | 25 ++ 9 files changed, 848 insertions(+), 55 deletions(-) create mode 100644 functional_tests/k8sevents_test.go create mode 100644 functional_tests/testdata_k8sevents/expected_k8sevents.yaml create mode 100644 functional_tests/testdata_k8sevents/k8sevents_values.yaml.tmpl create mode 100644 functional_tests/testdata_k8sevents/testobjects/1-serviceaccount.yaml create mode 100644 functional_tests/testdata_k8sevents/testobjects/2-svc.yaml create mode 100644 functional_tests/testdata_k8sevents/testobjects/3-sts.yaml diff --git a/.github/workflows/functional_test_v2.yaml b/.github/workflows/functional_test_v2.yaml index 4ed48c26c..4a76d4dd0 100644 --- a/.github/workflows/functional_test_v2.yaml +++ b/.github/workflows/functional_test_v2.yaml @@ -44,6 +44,7 @@ jobs: - functional - histogram - configuration_switching + - k8sevents - istio runs-on: ubuntu-latest steps: diff --git a/functional_tests/common.go b/functional_tests/common.go index 7941cef9f..6c57f4bb1 100644 --- a/functional_tests/common.go +++ b/functional_tests/common.go @@ -10,9 +10,13 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/signalfxreceiver" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/receiver/receivertest" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" "os" "path/filepath" "runtime" @@ -106,6 +110,11 @@ func writeNewExpectedMetricsResult(t *testing.T, file string, metric *pmetric.Me require.NoError(t, golden.WriteMetrics(t, filepath.Join("results", filepath.Base(file)), *metric)) } +func writeNewExpectedLogsResult(t *testing.T, file string, log *plog.Logs) { + require.NoError(t, os.MkdirAll("results", 0755)) + require.NoError(t, golden.WriteLogs(t, filepath.Join("results", filepath.Base(file)), *log)) +} + func setupSignalfxReceiver(t *testing.T, port int) *consumertest.MetricsSink { mc := new(consumertest.MetricsSink) f := signalfxreceiver.NewFactory() @@ -123,3 +132,57 @@ func setupSignalfxReceiver(t *testing.T, port int) *consumertest.MetricsSink { return mc } + +func checkPodsReady(t *testing.T, clientset *kubernetes.Clientset, namespace, labelSelector string, timeout time.Duration) { + require.Eventually(t, func() bool { + pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{ + LabelSelector: labelSelector, + }) + require.NoError(t, err) + if len(pods.Items) == 0 { + return false + } + for _, pod := range pods.Items { + if pod.Status.Phase != v1.PodRunning { + return false + } + ready := false + for _, condition := range pod.Status.Conditions { + if condition.Type == v1.PodReady && condition.Status == v1.ConditionTrue { + ready = true + break + } + } + if !ready { + return false + } + } + return true + }, timeout, 5*time.Second, "Pods in namespace %s with label %s are not ready", namespace, labelSelector) +} + +func createNamespace(t *testing.T, clientset *kubernetes.Clientset, name string) { + ns := &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + _, err := clientset.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}) + require.NoError(t, err, "failed to create namespace %s", name) + + require.Eventually(t, func() bool { + _, err := clientset.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}) + return err == nil + }, 1*time.Minute, 5*time.Second, "namespace %s is not available", name) +} + +func labelNamespace(t *testing.T, clientset *kubernetes.Clientset, name, key, value string) { + ns, err := clientset.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}) + require.NoError(t, err) + if ns.Labels == nil { + ns.Labels = make(map[string]string) + } + ns.Labels[key] = value + _, err = clientset.CoreV1().Namespaces().Update(context.TODO(), ns, metav1.UpdateOptions{}) + require.NoError(t, err) +} diff --git a/functional_tests/istio_test.go b/functional_tests/istio_test.go index dd988cb29..0ec4b2d4a 100644 --- a/functional_tests/istio_test.go +++ b/functional_tests/istio_test.go @@ -1,3 +1,5 @@ +// Copyright Splunk Inc. +// SPDX-License-Identifier: Apache-2.0 //go:build istio package functional_tests @@ -29,7 +31,6 @@ import ( "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/kube" - "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" @@ -272,34 +273,6 @@ func downloadIstio(t *testing.T, version string) string { return istioctlPath } -func checkPodsReady(t *testing.T, clientset *kubernetes.Clientset, namespace, labelSelector string, timeout time.Duration) { - require.Eventually(t, func() bool { - pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{ - LabelSelector: labelSelector, - }) - require.NoError(t, err) - if len(pods.Items) == 0 { - return false - } - for _, pod := range pods.Items { - if pod.Status.Phase != v1.PodRunning { - return false - } - ready := false - for _, condition := range pod.Status.Conditions { - if condition.Type == v1.PodReady && condition.Status == v1.ConditionTrue { - ready = true - break - } - } - if !ready { - return false - } - } - return true - }, timeout, 5*time.Second, "Pods in namespace %s with label %s are not ready", namespace, labelSelector) -} - func runCommand(t *testing.T, command string) { cmd := exec.Command("sh", "-c", command) cmd.Stdout = os.Stdout @@ -348,32 +321,6 @@ func createObjectFromURL(t *testing.T, config string, url string) { } } -func createNamespace(t *testing.T, clientset *kubernetes.Clientset, name string) { - ns := &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - } - _, err := clientset.CoreV1().Namespaces().Create(context.TODO(), ns, metav1.CreateOptions{}) - require.NoError(t, err, "failed to create namespace %s", name) - - require.Eventually(t, func() bool { - _, err := clientset.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}) - return err == nil - }, 1*time.Minute, 5*time.Second, "namespace %s is not available", name) -} - -func labelNamespace(t *testing.T, clientset *kubernetes.Clientset, name, key, value string) { - ns, err := clientset.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}) - require.NoError(t, err) - if ns.Labels == nil { - ns.Labels = make(map[string]string) - } - ns.Labels[key] = value - _, err = clientset.CoreV1().Namespaces().Update(context.TODO(), ns, metav1.UpdateOptions{}) - require.NoError(t, err) -} - func sendHTTPRequest(t *testing.T, client *http.Client, url, host, header, path string) { req, err := http.NewRequest("GET", url, nil) require.NoError(t, err) diff --git a/functional_tests/k8sevents_test.go b/functional_tests/k8sevents_test.go new file mode 100644 index 000000000..923f3d1c1 --- /dev/null +++ b/functional_tests/k8sevents_test.go @@ -0,0 +1,311 @@ +// Copyright Splunk Inc. +// SPDX-License-Identifier: Apache-2.0 + +//go:build k8events + +package functional_tests + +import ( + "bytes" + "context" + "fmt" + "os" + "path/filepath" + "regexp" + "sync" + "testing" + "text/template" + "time" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/plogtest" + k8stest "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/xk8stest" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/splunkhecreceiver" + "github.com/signalfx/splunk-otel-collector-chart/functional_tests/internal" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/receiver/receivertest" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/kube" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/yaml" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +const ( + apiPort = 8881 + splunkHecReceiverPort = 8089 +) + +var setupRun = sync.Once{} +var eventsLogsConsumer *consumertest.LogsSink + +// Env vars to control the test behavior +// TEARDOWN_BEFORE_SETUP: if set to true, the test will run teardown before setup +// SKIP_SETUP: if set to true, the test will skip setup +// SKIP_TEARDOWN: if set to true, the test will skip teardown +// SKIP_TESTS: if set to true, the test will skip the test +// UPDATE_EXPECTED_RESULTS: if set to true, the test will update the expected results +// KUBECONFIG: the path to the kubeconfig file + +func Test_K8SEvents(t *testing.T) { + eventsLogsConsumer := setup(t) + if os.Getenv("SKIP_TESTS") == "true" { + t.Log("Skipping tests as SKIP_TESTS is set to true") + return + } + + selectedLogs := selectLogs(t, "k8s.namespace.name", "k8sevents-test", eventsLogsConsumer, func(body string) string { + re := regexp.MustCompile(`Successfully pulled image "busybox:latest" in \d+ms \(\d+ms including waiting\)`) + return re.ReplaceAllString(body, `Successfully pulled image "busybox:latest" in