Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/oats-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: OATS Tests

on:
pull_request:
branches:
- main
paths:
- .mise.toml
- .github/workflows/oats-tests.yml
- cel-sampler/**
- .mise/tasks/oats-tests.sh
workflow_dispatch:

permissions:
contents: read

jobs:
acceptance-tests:
runs-on: ubuntu-24.04
steps:
- name: Check out
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1

- name: Set up JDK for running Gradle
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
with:
distribution: temurin
java-version: 17

- name: Set up gradle
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
with:
cache-read-only: ${{ github.event_name == 'pull_request' }}

- uses: jdx/mise-action@146a28175021df8ca24f8ee1828cc2a60f980bd5 # v3.5.1

- name: Run OATS tests
run: mise run oats-tests
14 changes: 14 additions & 0 deletions .mise/tasks/oats-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
#MISE description="Run OATS tests for cel-sampler"

set -euo pipefail

echo "==> Building cel-sampler shadow JAR..."
./gradlew :cel-sampler:shadowJar

echo "==> Building test application..."
# testapp is a standalone Gradle project, needs to be built separately
(cd cel-sampler/testapp && ../../gradlew jar)

echo "==> Running OATS integration tests..."
(cd cel-sampler && oats -timeout 5m oats/oats.yaml)
30 changes: 30 additions & 0 deletions cel-sampler/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id("otel.java-conventions")
id("otel.publish-conventions")
id("com.gradleup.shadow")
}

description = "Sampler which makes its decision based on semantic attributes values"
Expand All @@ -18,3 +19,32 @@ dependencies {
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
}

tasks {
shadowJar {
/**
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* Shaded version of this extension is required when using it as a OpenTelemetry Java Agent
* extension. Shading bundles the dependencies required by this extension in the resulting JAR,
* ensuring their presence on the classpath at runtime.
*
* See http://gradleup.com/shadow/introduction/#introduction for reference.
*/
archiveClassifier.set("shadow")
}

jar {
/**
* We need to publish both - shaded and unshaded variants of the dependency
* Shaded dependency is required for use with the Java agent.
* Unshaded dependency can be used with OTel Autoconfigure module.
*
* Not overriding the classifier to empty results in an implicit classifier 'plain' being
* used with the standard JAR.
*/
archiveClassifier.set("")
}

assemble {
dependsOn(shadowJar)
}
}
2 changes: 2 additions & 0 deletions cel-sampler/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# TODO: uncomment when ready to mark as stable
# otel.stable=true
24 changes: 24 additions & 0 deletions cel-sampler/oats/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM eclipse-temurin:21-jre

WORKDIR /usr/src/app/

# renovate: datasource=github-releases depName=open-telemetry/opentelemetry-java-instrumentation
ENV OPENTELEMETRY_JAVA_INSTRUMENTATION_VERSION=v2.22.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using FROM is more cache friendly and renovate works out of the box

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i might be misunderstanding something, but what would i be using as the base image using "FROM"? this is pulling the agent

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the grafana one is pulling a docker image, it is not pulling the agent directly.

Doing something like this doesn't work/make sense since it isn't a container:

ARG OPENTELEMETRY_JAVA_INSTRUMENTATION_VERSION=v2.22.0
FROM github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/$OPENTELEMETRY_JAVA_INSTRUMENTATION_VERSION/opentelemetry-javaagent.jar as agent

I'm not aware of a container I could use to pull with the agent, and I'm not sure if it would be beneficial compared to just pulling it directly like this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


COPY ./cel-sampler/testapp/build/libs/testapp.jar ./app.jar

# Add the CEL sampler extension JAR
COPY ./cel-sampler/build/libs/*-shadow.jar ./extensions/

COPY ./cel-sampler/oats/otel-config.yaml ./otel-config.yaml

ADD --chmod=644 https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/$OPENTELEMETRY_JAVA_INSTRUMENTATION_VERSION/opentelemetry-javaagent.jar ./opentelemetry-javaagent.jar

# Configure the Java agent with the CEL sampler extension
ENV JAVA_TOOL_OPTIONS=-javaagent:./opentelemetry-javaagent.jar
ENV OTEL_JAVAAGENT_EXTENSIONS=/usr/src/app/extensions/

ENV OTEL_EXPERIMENTAL_CONFIG_FILE=./otel-config.yaml

EXPOSE 8080
ENTRYPOINT [ "java", "-jar", "./app.jar" ]
25 changes: 25 additions & 0 deletions cel-sampler/oats/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# CEL Sampler OATS Integration Tests

This directory contains acceptance tests for the CEL-based sampler extension using the
[OATS (OpenTelemetry Acceptance Test Suite)](https://github.com/grafana/oats) framework.

## Overview

These tests verify that the CEL sampler correctly:

- Drops traces for health check endpoints (`/healthcheck`, `/metrics`)
- Samples traces for regular API endpoints (`/hello`, `/api/data`)
- Loads correctly as a Java agent extension
- Works with declarative configuration

## Running the Tests

You can build all assets and run tests using:

`mise run oats-test`

Or manually run just the tests (from the `cel-sampler` directory):

```bash
~/go/bin/oats oats/oats.yaml
```
12 changes: 12 additions & 0 deletions cel-sampler/oats/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: '3'
services:
app:
build:
context: ../../
dockerfile: cel-sampler/oats/Dockerfile
environment:
OTEL_SERVICE_NAME: "cel-sampler-test-app"
OTEL_EXPORTER_OTLP_ENDPOINT: http://lgtm:4318
OTEL_EXPERIMENTAL_CONFIG_FILE: ./otel-config.yaml
ports:
- "8080:8080"
34 changes: 34 additions & 0 deletions cel-sampler/oats/oats.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# OATS is an acceptance testing framework for OpenTelemetry - https://github.com/grafana/oats
oats-schema-version: 2

docker-compose:
files:
- ./docker-compose.yml

input:
- path: /hello
- path: /api/data
- path: /healthcheck
- path: /metrics

expected:
traces:
# Verify that /hello and /api/data endpoints are sampled
- traceql: '{ span.http.route = "/hello" }'
equals: "GET /hello"
attributes:
http.request.method: "GET"
http.route: "/hello"
- traceql: '{ span.http.route = "/api/data" }'
equals: "GET /api/data"
attributes:
http.request.method: "GET"
http.response.status_code: 200

# Verify that health check and metrics endpoints are dropped:
- traceql: '{ span.http.route = "/healthcheck" }'
count:
max: 0
- traceql: '{ span.http.route = "/metrics" }'
count:
max: 0
27 changes: 27 additions & 0 deletions cel-sampler/oats/otel-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# OpenTelemetry Declarative Configuration for CEL-based Sampler Integration Test

file_format: "1.0-rc.2"

resource:
attributes:
- name: service.name
value: cel-sampler-test

tracer_provider:
processors:
- batch:
exporter:
otlp_http:
endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://localhost:4318}/v1/traces
sampler:
parent_based:
root:
cel_based:
expressions:
- expression: 'attribute["url.path"] == "/healthcheck"'
action: DROP
- expression: 'attribute["url.path"] == "/metrics"'
action: DROP
# Fallback sampler: sample everything else (including /api/ endpoints)
fallback_sampler:
always_on: {}
2 changes: 2 additions & 0 deletions cel-sampler/testapp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build/
.gradle/
16 changes: 16 additions & 0 deletions cel-sampler/testapp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
java
application
}

application {
mainClass.set("io.opentelemetry.contrib.sampler.cel.testapp.SimpleServer")
}

tasks.jar {
manifest {
attributes["Main-Class"] = "io.opentelemetry.contrib.sampler.cel.testapp.SimpleServer"
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
}
1 change: 1 addition & 0 deletions cel-sampler/testapp/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "testapp"
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.sampler.cel.testapp;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.logging.Logger;

/**
* Simple HTTP server for testing CEL-based sampler extension.
*
* <p>This application provides multiple endpoints to test different sampling behaviors:
* - /api/data - Should be sampled (API endpoint)
* - /healthcheck - Should be dropped (health check endpoint)
* - /metrics - Should be dropped (metrics endpoint)
* - /hello - Should be sampled (regular endpoint)
*/
public class SimpleServer {

private static final Logger logger = Logger.getLogger(SimpleServer.class.getName());

public static void main(String[] args) throws Exception {
int port = 8080;
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

// Regular endpoints that should be sampled
server.createContext("/hello", new ResponseHandler("Hello from test app!"));
server.createContext("/api/data", new ResponseHandler("API data response"));

// Health/monitoring endpoints that should be dropped
server.createContext("/healthcheck", new ResponseHandler("OK"));
server.createContext("/metrics", new ResponseHandler("metrics=1"));

server.setExecutor(null);

logger.info("Starting server on port " + port);
server.start();
}

static class ResponseHandler implements HttpHandler {
private final String response;

ResponseHandler(String response) {
this.response = response;
}

@Override
public void handle(HttpExchange exchange) throws IOException {
byte[] responseBytes = response.getBytes(StandardCharsets.UTF_8);

exchange.sendResponseHeaders(200, responseBytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(responseBytes);
}

logger.info("Handled request to " + exchange.getRequestURI().getPath());
}
}
}
2 changes: 2 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[tools]
lychee = "0.22.0"
markdownlint-cli2 = "0.20.0"
"go:github.com/grafana/oats" = "0.6.0"

[settings]
# Only install tools explicitly defined in the [tools] section above
Expand All @@ -10,4 +11,5 @@ idiomatic_version_file_enable_tools = []
# Based on: https://github.com/jdx/mise/discussions/4461
windows_executable_extensions = ["sh"]
windows_default_file_shell_args = "bash"
unix_default_file_shell_args = "bash"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's that needed for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was getting this:

[oats-tests] $ ~/work/opentelemetry-java-contrib/opentelemetry-java-contrib/.mise/tasks/oats-tests.sh
/home/runner/work/opentelemetry-java-contrib/opentelemetry-java-contrib/.mise/tasks/oats-tests.sh: 4: set: Illegal option -o pipefail

use_file_shell_for_executable_tasks = true