Skip to content

Commit

Permalink
.NET Functional Tests (#1276)
Browse files Browse the repository at this point in the history
  • Loading branch information
jvoravong authored Jun 3, 2024
1 parent 5612a7f commit 9596f7e
Show file tree
Hide file tree
Showing 12 changed files with 1,282 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: dotnet-test
spec:
replicas: 1
selector:
matchLabels:
app: dotnet-test
template:
metadata:
name: dotnet-test
labels:
app: dotnet-test
spec:
containers:
- image: quay.io/splunko11ytest/dotnet_test:latest
name: dotnet-test
imagePullPolicy: IfNotPresent
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ kubectl create namespace dotnet-demo
```

```bash
curl https://raw.githubusercontent.com/signalfx/splunk-otel-collector-chart/main/functional_tests/testdata/dotnet/deployment.yaml | kubectl apply -n dotnet-demo -f -
curl https://raw.githubusercontent.com/signalfx/splunk-otel-collector-chart/main/examples/enable-operator-and-auto-instrumentation/dotnet/deployment.yaml | kubectl apply -n dotnet-demo -f -
```

### 2. Complete the steps outlined in [Getting started with auto-instrumentation](../../docs/auto-instrumentation-install.md#steps-for-setting-up-auto-instrumentation)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ spec:
image: ghcr.io/signalfx/splunk-otel-dotnet/splunk-otel-dotnet:v1.5.0
env:
- name: OTEL_DOTNET_AUTO_PLUGINS
value: "Splunk.OpenTelemetry.AutoInstrumentation.Plugin, Splunk.OpenTelemetry.AutoInstrumentation"
value: "Splunk.OpenTelemetry.AutoInstrumentation.Plugin,Splunk.OpenTelemetry.AutoInstrumentation"
- name: OTEL_RESOURCE_ATTRIBUTES
value: splunk.zc.method=splunk-otel-dotnet:v1.5.0
- name: SPLUNK_OTEL_AGENT
Expand Down
98 changes: 98 additions & 0 deletions functional_tests/functional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const (
signalFxReceiverPort = 9443
signalFxReceiverK8sClusterReceiverPort = 19443
otlpReceiverPort = 4317
otlpHTTPReceiverPort = 4318
apiPort = 8881
kindTestKubeEnv = "kind"
eksTestKubeEnv = "eks"
Expand Down Expand Up @@ -209,6 +210,7 @@ func deployChartsAndApps(t *testing.T) {
deployments := clientset.AppsV1().Deployments("default")

decode := scheme.Codecs.UniversalDeserializer().Decode
// NodeJS test app
stream, err := os.ReadFile(filepath.Join(testDir, "nodejs", "deployment.yaml"))
require.NoError(t, err)
deployment, _, err := decode(stream, nil, nil)
Expand All @@ -234,6 +236,19 @@ func deployChartsAndApps(t *testing.T) {
require.NoError(t, err)
}
}
// .NET test app
stream, err = os.ReadFile(filepath.Join(testDir, "dotnet", "deployment.yaml"))
require.NoError(t, err)
deployment, _, err = decode(stream, nil, nil)
require.NoError(t, err)
_, err = deployments.Create(context.Background(), deployment.(*appsv1.Deployment), metav1.CreateOptions{})
if err != nil {
_, err2 := deployments.Update(context.Background(), deployment.(*appsv1.Deployment), metav1.UpdateOptions{})
assert.NoError(t, err2)
if err2 != nil {
require.NoError(t, err)
}
}
jobstream, err := os.ReadFile(filepath.Join(testDir, manifestsDir, "test_jobs.yaml"))
require.NoError(t, err)
var namespaces []*corev1.Namespace
Expand Down Expand Up @@ -315,6 +330,9 @@ func teardown(t *testing.T) {
_ = deployments.Delete(context.Background(), "java-test", metav1.DeleteOptions{
GracePeriodSeconds: &waitTime,
})
_ = deployments.Delete(context.Background(), "dotnet-test", metav1.DeleteOptions{
GracePeriodSeconds: &waitTime,
})
jobstream, err := os.ReadFile(filepath.Join(testDir, manifestsDir, "test_jobs.yaml"))
require.NoError(t, err)
var namespaces []*corev1.Namespace
Expand Down Expand Up @@ -388,6 +406,7 @@ func Test_Functions(t *testing.T) {

t.Run("node.js traces captured", testNodeJSTraces)
t.Run("java traces captured", testJavaTraces)
t.Run(".NET traces captured", testDotNetTraces)
t.Run("kubernetes cluster metrics", testK8sClusterReceiverMetrics)
t.Run("agent logs", testAgentLogs)
t.Run("test HEC metrics", testHECMetrics)
Expand Down Expand Up @@ -512,6 +531,70 @@ func testJavaTraces(t *testing.T) {
require.NoError(t, err)
}

func testDotNetTraces(t *testing.T) {
tracesConsumer := setupOnce(t).tracesConsumer

var expectedTraces ptrace.Traces
expectedTracesFile := filepath.Join(testDir, expectedValuesDir, "expected_dotnet_traces.yaml")
expectedTraces, err := golden.ReadTraces(expectedTracesFile)
require.NoError(t, err)

waitForTraces(t, 30, tracesConsumer)
var selectedTrace *ptrace.Traces

require.Eventually(t, func() bool {
for i := len(tracesConsumer.AllTraces()) - 1; i > 0; i-- {
trace := tracesConsumer.AllTraces()[i]
if val, ok := trace.ResourceSpans().At(0).Resource().Attributes().Get("telemetry.sdk.language"); ok && strings.Contains(val.Str(), "dotnet") {
if expectedTraces.SpanCount() == trace.SpanCount() {
selectedTrace = &trace
break
}
selectedTrace = &trace
break
}
}
return selectedTrace != nil
}, 3*time.Minute, 5*time.Second)

require.NotNil(t, selectedTrace)

maskScopeVersion(*selectedTrace)
maskScopeVersion(expectedTraces)
maskSpanParentID(*selectedTrace)
maskSpanParentID(expectedTraces)

err = ptracetest.CompareTraces(expectedTraces, *selectedTrace,
ptracetest.IgnoreResourceAttributeValue("os.description"),
ptracetest.IgnoreResourceAttributeValue("process.pid"),
ptracetest.IgnoreResourceAttributeValue("container.id"),
ptracetest.IgnoreResourceAttributeValue("k8s.deployment.name"),
ptracetest.IgnoreResourceAttributeValue("k8s.pod.ip"),
ptracetest.IgnoreResourceAttributeValue("k8s.pod.name"),
ptracetest.IgnoreResourceAttributeValue("k8s.pod.uid"),
ptracetest.IgnoreResourceAttributeValue("k8s.replicaset.name"),
ptracetest.IgnoreResourceAttributeValue("os.version"),
ptracetest.IgnoreResourceAttributeValue("host.arch"),
ptracetest.IgnoreResourceAttributeValue("telemetry.distro.version"),
ptracetest.IgnoreResourceAttributeValue("telemetry.sdk.version"),
ptracetest.IgnoreResourceAttributeValue("telemetry.auto.version"),
ptracetest.IgnoreResourceAttributeValue("splunk.distro.version"),
ptracetest.IgnoreResourceAttributeValue("splunk.zc.method"),
ptracetest.IgnoreSpanAttributeValue("net.sock.peer.port"),
ptracetest.IgnoreSpanAttributeValue("thread.id"),
ptracetest.IgnoreSpanAttributeValue("thread.name"),
ptracetest.IgnoreSpanAttributeValue("os.version"),
ptracetest.IgnoreTraceID(),
ptracetest.IgnoreSpanID(),
ptracetest.IgnoreStartTimestamp(),
ptracetest.IgnoreEndTimestamp(),
ptracetest.IgnoreResourceSpansOrder(),
ptracetest.IgnoreScopeSpansOrder(),
)

require.NoError(t, err)
}

func shortenNames(value string) string {
if strings.HasPrefix(value, "kube-proxy") {
return "kube-proxy"
Expand Down Expand Up @@ -1094,6 +1177,7 @@ func setupTraces(t *testing.T) *consumertest.TracesSink {
f := otlpreceiver.NewFactory()
cfg := f.CreateDefaultConfig().(*otlpreceiver.Config)
cfg.Protocols.GRPC.NetAddr.Endpoint = fmt.Sprintf("0.0.0.0:%d", otlpReceiverPort)
cfg.Protocols.HTTP.Endpoint = fmt.Sprintf("0.0.0.0:%d", otlpHTTPReceiverPort)

rcvr, err := f.CreateTracesReceiver(context.Background(), receivertest.NewNopCreateSettings(), cfg, tc)
require.NoError(t, err)
Expand Down Expand Up @@ -1262,3 +1346,17 @@ func maskScopeVersion(traces ptrace.Traces) {
}
}
}

func maskSpanParentID(traces ptrace.Traces) {
rss := traces.ResourceSpans()
for i := 0; i < rss.Len(); i++ {
rs := rss.At(i)
for j := 0; j < rs.ScopeSpans().Len(); j++ {
ss := rs.ScopeSpans().At(j)
for k := 0; k < ss.Spans().Len(); k++ {
span := ss.Spans().At(k)
span.SetParentSpanID(pcommon.NewSpanIDEmpty())
}
}
}
}
2 changes: 1 addition & 1 deletion functional_tests/testdata/dotnet/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Default image repository
IMAGE_REPO ?= quay.io/splunko11ytest/dotnet_test

# This .NET application cannot run on arm machines due to .NET runtime limitations
# This .NET application cannot currently run on arm machines due to .NET runtime environment limitations
# Was not able to use docker buildx with .NET
.PHONY: push
push:
Expand Down
40 changes: 40 additions & 0 deletions functional_tests/testdata/dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,43 @@ Running this container inside a Kubernetes cluster under observation of the oper
## Develop

Login to quay.io and push with `make push`
Make sure for new image repositories you make the repository public
- Arm based machines can have issues running docker/dockerx build commands that use QEMU virtualization and have .NET support, see: https://github.com/dotnet/dotnet-docker/issues/3832
- On an arm M2 Mac running Docker Desktop 25.0.0, was able to get the docker/dockerx build for 1 arch images but not multi arch images.
- New versions of the OS with Docker may fix this issue.
- Dockerx related command: `docker buildx create --use --name multi-arch-builder`

### Debugging .NET

These env vars can be set to help debug .NET instrumentation

```yaml
env:
- name: OTEL_LOG_LEVEL
value: DEBUG
- name: OTEL_DOTNET_AUTO_TRACES_CONSOLE_EXPORTER_ENABLED
value: "true"
```
### Current issues and workarounds
#### Rule Engine Failure - OTL-2843
An env var may be needed to bypass an error thrown by auto-instrumentation.
Error:
```
[Error] Error in StartupHook initialization: LoaderFolderLocation: /otel-auto-instrumentation-dotnet/net
Exception: Rule Engine Failure: One or more rules failed validation. Automatic Instrumentation won't be loaded.
System.Exception: Rule Engine Failure: One or more rules failed validation. Automatic Instrumentation won't be loaded.
at StartupHook.Initialize() in /_/src/OpenTelemetry.AutoInstrumentation.StartupHook/StartupHook.cs:line 34
```
Env var:
```yaml
env:
- name: OTEL_DOTNET_AUTO_RULE_ENGINE_ENABLED
value: "false"
```
2 changes: 1 addition & 1 deletion functional_tests/testdata/dotnet/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ spec:
app: dotnet-test
annotations:
instrumentation.opentelemetry.io/inject-dotnet: "true"
instrumentation.opentelemetry.io/otel-dotnet-auto-runtime: "linux-x64"
instrumentation.opentelemetry.io/otel-dotnet-auto-runtime: "linux-musl-x64"
spec:
containers:
- image: quay.io/splunko11ytest/dotnet_test:latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,30 @@ resourceMetrics:
value:
stringValue: k8scluster
timeUnixNano: "1000000"
- asInt: "2"
attributes:
- key: k8s.cluster.name
value:
stringValue: rotel-eks
- key: k8s.namespace.name
value:
stringValue: default
- key: k8s.node.name
value:
stringValue: kind-control-plane
- key: k8s.pod.name
value:
stringValue: dotnet-test-57564b7dc9-pf2bk
- key: k8s.pod.uid
value:
stringValue: b660efd2-e961-488a-af7a-2161f27565bb
- key: metric_source
value:
stringValue: kubernetes
- key: receiver
value:
stringValue: k8scluster
timeUnixNano: "1000000"
- asInt: "2"
attributes:
- key: k8s.cluster.name
Expand Down Expand Up @@ -1150,6 +1174,42 @@ resourceMetrics:
value:
stringValue: k8scluster
timeUnixNano: "1000000"
- asInt: "1"
attributes:
- key: container.id
value:
stringValue: 38809b5e5212eddddeec2d4d387e2f27ff754e0c3f67cc9a9ffb89db72601480
- key: container.image.name
value:
stringValue: quay.io/splunko11ytest/dotnet_test
- key: container.image.tag
value:
stringValue: latest
- key: k8s.cluster.name
value:
stringValue: rotel-eks
- key: k8s.container.name
value:
stringValue: dotnet-test
- key: k8s.namespace.name
value:
stringValue: default
- key: k8s.node.name
value:
stringValue: kind-control-plane
- key: k8s.pod.name
value:
stringValue: dotnet-test-57564b7dc9-pf2bk
- key: k8s.pod.uid
value:
stringValue: b660efd2-e961-488a-af7a-2161f27565bb
- key: metric_source
value:
stringValue: kubernetes
- key: receiver
value:
stringValue: k8scluster
timeUnixNano: "1000000"
- asInt: "1"
attributes:
- key: container.id
Expand Down Expand Up @@ -3916,6 +3976,27 @@ resourceMetrics:
value:
stringValue: k8scluster
timeUnixNano: "1000000"
- asInt: "1"
attributes:
- key: k8s.cluster.name
value:
stringValue: rotel-eks
- key: k8s.namespace.name
value:
stringValue: default
- key: k8s.replicaset.name
value:
stringValue: dotnet-test-57564b7dc9
- key: k8s.replicaset.uid
value:
stringValue: fc4c748c-a646-4813-9abf-f0688a15d022
- key: metric_source
value:
stringValue: kubernetes
- key: receiver
value:
stringValue: k8scluster
timeUnixNano: "1000000"
- asInt: "1"
attributes:
- key: k8s.cluster.name
Expand Down Expand Up @@ -4087,6 +4168,27 @@ resourceMetrics:
value:
stringValue: k8scluster
timeUnixNano: "1000000"
- asInt: "1"
attributes:
- key: k8s.cluster.name
value:
stringValue: rotel-eks
- key: k8s.namespace.name
value:
stringValue: default
- key: k8s.replicaset.name
value:
stringValue: dotnet-test-57564b7dc9
- key: k8s.replicaset.uid
value:
stringValue: fc4c748c-a646-4813-9abf-f0688a15d022
- key: metric_source
value:
stringValue: kubernetes
- key: receiver
value:
stringValue: k8scluster
timeUnixNano: "1000000"
- asInt: "1"
attributes:
- key: k8s.cluster.name
Expand Down
Loading

0 comments on commit 9596f7e

Please sign in to comment.