Skip to content

Commit

Permalink
test: Telemetry OTLP-based components error/warning logs (#931)
Browse files Browse the repository at this point in the history
  • Loading branch information
TeodorSAP authored Apr 4, 2024
1 parent c9b5db0 commit e6dfed3
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 46 deletions.
24 changes: 23 additions & 1 deletion .github/workflows/pr-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,28 @@ jobs:
with:
failure: failure()

e2e-telemetry-logs-analysis:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Prepare Test
uses: "./.github/template/prepare-test"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Run tests
run: |
hack/deploy-istio.sh
bin/ginkgo run --tags e2e --label-filter="telemetry-logs-analysis" test/e2e
- name: Finalize Test
uses: "./.github/template/finalize-test"
if: success() || failure()
with:
failure: failure()

e2e-integration-istio:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -212,7 +234,7 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Run tests
run: bin/ginkgo run --tags e2e --label-filter="self-mon-traces" test/e2e
run: bin/ginkgo run --tags e2e --label-filter="self-mon-traces" test/e2e

- name: Finalize Test
uses: "./.github/template/finalize-test"
Expand Down
166 changes: 166 additions & 0 deletions test/e2e/telemetry_logs_analysis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//go:build e2e

package e2e

import (
"fmt"
"net/http"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/format"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

kitk8s "github.com/kyma-project/telemetry-manager/test/testkit/k8s"
kitkyma "github.com/kyma-project/telemetry-manager/test/testkit/kyma"
. "github.com/kyma-project/telemetry-manager/test/testkit/matchers/log"
"github.com/kyma-project/telemetry-manager/test/testkit/mocks/backend"
"github.com/kyma-project/telemetry-manager/test/testkit/mocks/telemetrygen"
"github.com/kyma-project/telemetry-manager/test/testkit/mocks/trafficgen"
"github.com/kyma-project/telemetry-manager/test/testkit/periodic"
"github.com/kyma-project/telemetry-manager/test/testkit/verifiers"
)

var _ = Describe("Telemetry Components Error/Warning Logs Analysis", Label("telemetry-logs-analysis"), Ordered, func() {
const (
mockNs = "tlogs-http"
otelCollectorLogBackendName = "tlogs-log-otlp"
metricBackendName = "tlogs-metric"
traceBackendName = "tlogs-trace"
pushMetricsDepName = "push-metrics-istiofied"
)

var (
otelCollectorLogPipelineName string
metricPipelineName string
tracePipelineName string
otelCollectorLogTelemetryExportURL string
metricTelemetryExportURL string
traceTelemetryExportURL string
gomegaMaxLength = format.MaxLength
errorWarningLevels = []string{
"ERROR", "error",
"WARNING", "warning",
"WARN", "warn"}
)

makeResources := func() []client.Object {
var objs []client.Object
objs = append(objs, kitk8s.NewNamespace(mockNs).K8sObject())

// backends
otelCollectorLogBackend := backend.New(otelCollectorLogBackendName, mockNs, backend.SignalTypeLogs)
objs = append(objs, otelCollectorLogBackend.K8sObjects()...)
otelCollectorLogTelemetryExportURL = otelCollectorLogBackend.TelemetryExportURL(proxyClient)
metricBackend := backend.New(metricBackendName, mockNs, backend.SignalTypeMetrics)
metricTelemetryExportURL = metricBackend.TelemetryExportURL(proxyClient)
objs = append(objs, metricBackend.K8sObjects()...)
traceBackend := backend.New(traceBackendName, mockNs, backend.SignalTypeTraces)
traceTelemetryExportURL = traceBackend.TelemetryExportURL(proxyClient)
objs = append(objs, traceBackend.K8sObjects()...)

// log pipelines
otelCollectorLogPipeline := kitk8s.NewLogPipelineV1Alpha1(fmt.Sprintf("%s-pipeline", otelCollectorLogBackend.Name())).
WithSecretKeyRef(otelCollectorLogBackend.HostSecretRefV1Alpha1()).
WithHTTPOutput().
WithIncludeNamespaces([]string{kitkyma.SystemNamespaceName}).
WithIncludeContainers([]string{"collector"})
otelCollectorLogPipelineName = otelCollectorLogPipeline.Name()
objs = append(objs, otelCollectorLogPipeline.K8sObject())
// TODO: Separate FluentBit logPipeline (CONTAINERS: fluent-bit, exporter)

// metrics & traces
metricPipeline := kitk8s.NewMetricPipelineV1Alpha1(fmt.Sprintf("%s-pipeline", metricBackend.Name())).
WithOutputEndpointFromSecret(metricBackend.HostSecretRefV1Alpha1()).
PrometheusInput(true, kitk8s.IncludeNamespacesV1Alpha1(mockNs)).
IstioInput(true, kitk8s.IncludeNamespacesV1Alpha1(mockNs)).
OtlpInput(true).
RuntimeInput(true, kitk8s.IncludeNamespacesV1Alpha1(mockNs))
metricPipelineName = metricPipeline.Name()
objs = append(objs, metricPipeline.K8sObject())
tracePipeline := kitk8s.NewTracePipelineV1Alpha1(fmt.Sprintf("%s-pipeline", traceBackend.Name())).
WithOutputEndpointFromSecret(traceBackend.HostSecretRefV1Alpha1())
tracePipelineName = tracePipeline.Name()
objs = append(objs, tracePipeline.K8sObject())

// metrics istio set-up (src/dest pods)
objs = append(objs, trafficgen.K8sObjects(mockNs)...)

// metric istio set-up (telemetrygen)
objs = append(objs,
kitk8s.NewPod("telemetrygen-metrics", mockNs).WithPodSpec(telemetrygen.PodSpec(telemetrygen.SignalTypeMetrics, "")).K8sObject(),
kitk8s.NewPod("telemetrygen-traces", mockNs).WithPodSpec(telemetrygen.PodSpec(telemetrygen.SignalTypeTraces, "")).K8sObject(),
)

return objs
}

Context("When telemetry components are set-up", func() {
BeforeAll(func() {
format.MaxLength = 0 // remove Gomega truncation
k8sObjects := makeResources()
DeferCleanup(func() {
Expect(kitk8s.DeleteObjects(ctx, k8sClient, k8sObjects...)).Should(Succeed())
})
Expect(kitk8s.CreateObjects(ctx, k8sClient, k8sObjects...)).Should(Succeed())
})

It("Should have running metric and trace gateways", func() {
verifiers.DeploymentShouldBeReady(ctx, k8sClient, kitkyma.MetricGatewayName)
verifiers.DeploymentShouldBeReady(ctx, k8sClient, kitkyma.TraceGatewayName)
})

It("Should have running backends", func() {
verifiers.DeploymentShouldBeReady(ctx, k8sClient, types.NamespacedName{Namespace: mockNs, Name: otelCollectorLogBackendName})
verifiers.DeploymentShouldBeReady(ctx, k8sClient, types.NamespacedName{Namespace: mockNs, Name: metricBackendName})
verifiers.DeploymentShouldBeReady(ctx, k8sClient, types.NamespacedName{Namespace: mockNs, Name: traceBackendName})
})

It("Should have running pipelines", func() {
verifiers.LogPipelineShouldBeHealthy(ctx, k8sClient, otelCollectorLogPipelineName)
verifiers.MetricPipelineShouldBeHealthy(ctx, k8sClient, metricPipelineName)
verifiers.TracePipelineShouldBeHealthy(ctx, k8sClient, tracePipelineName)
})

It("Should have a running metric agent daemonset", func() {
verifiers.DaemonSetShouldBeReady(ctx, k8sClient, kitkyma.MetricAgentName)
})

It("Should push metrics successfully", func() {
verifiers.MetricsFromNamespaceShouldBeDelivered(proxyClient, metricTelemetryExportURL, mockNs, telemetrygen.MetricNames)
})

It("Should push traces successfully", func() {
verifiers.TracesFromNamespaceShouldBeDelivered(proxyClient, traceTelemetryExportURL, mockNs)
})

It("Should not have any ERROR/WARNING logs in the OTLP containers", func() {
Consistently(func(g Gomega) {
resp, err := proxyClient.Get(otelCollectorLogTelemetryExportURL)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(resp).To(HaveHTTPStatus(http.StatusOK))
g.Expect(resp).To(HaveHTTPBody(
Not(ContainLd(ContainLogRecord(SatisfyAll(
WithPodName(ContainSubstring("telemetry-")),
WithLevel(BeElementOf(errorWarningLevels)),
WithLogBody(Not( // whitelist possible (flaky/expected) errors
Or(
ContainSubstring("grpc: addrConn.createTransport failed to connect"),
),
)),
)))),
))
}, time.Second*120, periodic.TelemetryInterval).Should(Succeed())
})

// TODO: Should not have any ERROR/WARNING level logs in the FluentBit containers
// TODO: configmap: FLuentBit, exclude_path (excluding self logs)
// telemetry-manager/blob/test/check-error-logs/internal/fluentbit/config/builder/input.go#L15-L16

AfterAll(func() {
format.MaxLength = gomegaMaxLength // restore Gomega truncation
})
})
})
48 changes: 3 additions & 45 deletions test/integration/istio/metrics_istio_input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import (

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

kitk8s "github.com/kyma-project/telemetry-manager/test/testkit/k8s"
kitkyma "github.com/kyma-project/telemetry-manager/test/testkit/kyma"
. "github.com/kyma-project/telemetry-manager/test/testkit/matchers/metric"
"github.com/kyma-project/telemetry-manager/test/testkit/mocks/backend"
"github.com/kyma-project/telemetry-manager/test/testkit/mocks/trafficgen"
"github.com/kyma-project/telemetry-manager/test/testkit/periodic"
"github.com/kyma-project/telemetry-manager/test/testkit/verifiers"
)
Expand All @@ -25,8 +25,6 @@ var _ = Describe("Metrics Istio Input", Label("metrics"), func() {
backendName = "backend"
app1Ns = "app-1"
app2Ns = "app-2"
nginxImage = "europe-docker.pkg.dev/kyma-project/prod/external/nginx:1.23.3"
curlImage = "europe-docker.pkg.dev/kyma-project/prod/external/curlimages/curl:7.78.0"
)

// https://istio.io/latest/docs/reference/config/metrics/
Expand Down Expand Up @@ -72,39 +70,6 @@ var _ = Describe("Metrics Istio Input", Label("metrics"), func() {
telemetryExportURL string
)

sourcePodSpec := func() corev1.PodSpec {
return corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "source",
Image: curlImage,
Command: []string{
"/bin/sh",
"-c",
"while true; do curl http://destination:80; sleep 1; done",
},
},
},
}
}

destinationPodSpec := func() corev1.PodSpec {
return corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "destination",
Image: nginxImage,
Ports: []corev1.ContainerPort{
{
ContainerPort: 80,
Protocol: corev1.ProtocolTCP,
},
},
},
},
}
}

makeResources := func() []client.Object {
var objs []client.Object
objs = append(objs, kitk8s.NewNamespace(backendNs).K8sObject(),
Expand All @@ -121,15 +86,8 @@ var _ = Describe("Metrics Istio Input", Label("metrics"), func() {
IstioInput(true, kitk8s.IncludeNamespacesV1Alpha1(app1Ns))
objs = append(objs, metricPipeline.K8sObject())

source1 := kitk8s.NewPod("source", app1Ns).WithPodSpec(sourcePodSpec())
destination1 := kitk8s.NewPod("destination", app1Ns).WithPodSpec(destinationPodSpec()).WithLabel("app", "destination")
service1 := kitk8s.NewService("destination", app1Ns).WithPort("http", 80)

source2 := kitk8s.NewPod("source", app2Ns).WithPodSpec(sourcePodSpec())
destination2 := kitk8s.NewPod("destination", app2Ns).WithPodSpec(destinationPodSpec()).WithLabel("app", "destination")
service2 := kitk8s.NewService("destination", app2Ns).WithPort("http", 80)

objs = append(objs, source1.K8sObject(), destination1.K8sObject(), service1.K8sObject(kitk8s.WithLabel("app", "destination")), source2.K8sObject(), destination2.K8sObject(), service2.K8sObject(kitk8s.WithLabel("app", "destination")))
objs = append(objs, trafficgen.K8sObjects(app1Ns)...)
objs = append(objs, trafficgen.K8sObjects(app2Ns)...)

return objs
}
Expand Down
1 change: 1 addition & 0 deletions test/testkit/k8s/log_pipeline_v1alpha1.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type logPipelineV1Alpha1 struct {
dropLabels bool
output telemetryv1alpha1.Output
filters []telemetryv1alpha1.Filter
// TODO: Configure to also include FluentBit logs (2nd PR)
}

func NewLogPipelineV1Alpha1(name string) *logPipelineV1Alpha1 {
Expand Down
12 changes: 12 additions & 0 deletions test/testkit/matchers/log/log_matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,18 @@ var _ = Describe("WithLogRecordAttrs", func() {
})
})

var _ = Describe("WithLogBody", func() {
It("should apply matcher", func() {
ld := plog.NewLogs()
rl := ld.ResourceLogs().AppendEmpty()
lrs := rl.ScopeLogs().AppendEmpty().LogRecords()
lr := lrs.AppendEmpty()
lr.Body().SetStr("This is a test log body.")

Expect(mustMarshalLogs(ld)).Should(ContainLd(ContainLogRecord(WithLogBody(Equal("This is a test log body.")))))
})
})

func mustMarshalLogs(ld plog.Logs) []byte {
var marshaler plog.JSONMarshaler
bytes, err := marshaler.MarshalLogs(ld)
Expand Down
6 changes: 6 additions & 0 deletions test/testkit/matchers/log/log_matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,9 @@ func WithLogRecordAttrs(matcher types.GomegaMatcher) types.GomegaMatcher {
return lr.Attributes().AsRaw()
}, matcher)
}

func WithLogBody(matcher types.GomegaMatcher) types.GomegaMatcher {
return gomega.WithTransform(func(lr plog.LogRecord) string {
return lr.Body().AsString()
}, matcher)
}
60 changes: 60 additions & 0 deletions test/testkit/mocks/trafficgen/trafficgen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package trafficgen

import (
"fmt"

corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

kitk8s "github.com/kyma-project/telemetry-manager/test/testkit/k8s"
)

const (
nginxImage = "europe-docker.pkg.dev/kyma-project/prod/external/nginx:1.23.3"
curlImage = "europe-docker.pkg.dev/kyma-project/prod/external/curlimages/curl:7.78.0"
sourceName = "source"
destinationName = "destination"
destinationPort = 80
appLabelKey = "app"
)

func sourcePodSpec() corev1.PodSpec {
return corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "source",
Image: curlImage,
Command: []string{
"/bin/sh",
"-c",
fmt.Sprintf("while true; do curl http://%s:%d; sleep 1; done", destinationName, destinationPort),
},
},
},
}
}

func destinationPodSpec() corev1.PodSpec {
return corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "destination",
Image: nginxImage,
Ports: []corev1.ContainerPort{
{
ContainerPort: destinationPort,
Protocol: corev1.ProtocolTCP,
},
},
},
},
}
}

func K8sObjects(namespace string) []client.Object {
return []client.Object{
kitk8s.NewPod(sourceName, namespace).WithPodSpec(sourcePodSpec()).K8sObject(),
kitk8s.NewPod(destinationName, namespace).WithPodSpec(destinationPodSpec()).WithLabel(appLabelKey, destinationName).K8sObject(),
kitk8s.NewService(destinationName, namespace).WithPort("http", destinationPort).K8sObject(kitk8s.WithLabel(appLabelKey, destinationName)),
}
}

0 comments on commit e6dfed3

Please sign in to comment.