diff --git a/TESTING.md b/TESTING.md index 894df100be..445b4d3865 100644 --- a/TESTING.md +++ b/TESTING.md @@ -38,6 +38,7 @@ This is the entire checklist on what we should do to assert that Tracetest is wo - [ ] [Amazon X-Ray example](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-amazon-x-ray) - [ ] [Datadog example](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-datadog) + - [ ] [Dynatrace example](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-dynatrace) - [ ] [Elastic APM example](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-elasticapm) - [ ] [Lightstep example](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-lightstep) - [ ] [New Relic example](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-new-relic) diff --git a/api/dataStores.yaml b/api/dataStores.yaml index 20db262647..bb9e33b4d2 100644 --- a/api/dataStores.yaml +++ b/api/dataStores.yaml @@ -194,7 +194,8 @@ components: awsxray, honeycomb, azureappinsights, - signoz + signoz, + dynatrace ] SupportedClients: type: string diff --git a/cli/openapi/model_supported_data_stores.go b/cli/openapi/model_supported_data_stores.go index 9b76ceee3c..56168d40a0 100644 --- a/cli/openapi/model_supported_data_stores.go +++ b/cli/openapi/model_supported_data_stores.go @@ -33,6 +33,7 @@ const ( HONEYCOMB SupportedDataStores = "honeycomb" AZUREAPPINSIGHTS SupportedDataStores = "azureappinsights" SIGNOZ SupportedDataStores = "signoz" + DYNATRACE SupportedDataStores = "dynatrace" ) // All allowed values of SupportedDataStores enum @@ -50,6 +51,7 @@ var AllowedSupportedDataStoresEnumValues = []SupportedDataStores{ "honeycomb", "azureappinsights", "signoz", + "dynatrace", } func (v *SupportedDataStores) UnmarshalJSON(src []byte) error { diff --git a/docs/docs/cli/creating-data-stores.md b/docs/docs/cli/creating-data-stores.md index 16bffad558..b0db55ef55 100644 --- a/docs/docs/cli/creating-data-stores.md +++ b/docs/docs/cli/creating-data-stores.md @@ -121,6 +121,16 @@ spec: default: true ``` +### Dynatrace + +```yaml +type: DataStore +spec: + name: Dynatrace pipeline + type: dynatrace + default: true +``` + ### Honeycomb ```yaml diff --git a/docs/docs/configuration/connecting-to-data-stores/dynatrace.md b/docs/docs/configuration/connecting-to-data-stores/dynatrace.md new file mode 100644 index 0000000000..817197b04e --- /dev/null +++ b/docs/docs/configuration/connecting-to-data-stores/dynatrace.md @@ -0,0 +1,99 @@ +# Dynatrace + +If you want to use [Dynatrace](https://www.dynatrace.com/) as the trace data store, you'll configure the OpenTelemetry Collector to receive traces from your system and then send them to both Tracetest and Dynatrace. And, you don't have to change your existing pipelines to do so. + +:::tip +Examples of configuring Tracetest with Dynatrace can be found in the [`examples` folder of the Tracetest GitHub repo](https://github.com/kubeshop/tracetest/tree/main/examples). +::: + +## Configuring OpenTelemetry Collector to Send Traces to both Dynatrace and Tracetest + +In your OpenTelemetry Collector config file: + +- Set the `exporter` to `otlp/tracetest` +- Set the `endpoint` to your Tracetest instance on port `4317` + +:::tip +If you are running Tracetest with Docker, and Tracetest's service name is `tracetest`, then the endpoint might look like this `http://tracetest:4317` +::: + +Additionally, add another config: + +- Set the `exporter` to `otlp/dynatrace` +- Set the `endpoint` to your Dynatrace tenant and include the: `https://{your-environment-id}.live.dynatrace.com/api/v2/otlp` + +```yaml +# collector.config.yaml + +# If you already have receivers declared, you can just ignore +# this one and still use yours instead. +receivers: + otlp: + protocols: + grpc: + http: + +processors: + batch: + timeout: 100ms + +exporters: + logging: + verbosity: detailed + # OTLP for Tracetest + otlp/tracetest: + endpoint: tracetest:4317 # Send traces to Tracetest. Read more in docs here: https://docs.tracetest.io/configuration/connecting-to-data-stores/opentelemetry-collector + tls: + insecure: true + # OTLP for Dynatrace + otlp/dynatrace: + endpoint: https://abc123.live.dynatrace.com/api/v2/otlp # Send traces to Dynatrace. Read more in docs here: https://www.dynatrace.com/support/help/extend-dynatrace/opentelemetry/collector#configuration + headers: + Authorization: "Api-Token dt0c01.sample.secret" # Requires "openTelemetryTrace.ingest" permission +service: + pipelines: + traces/tracetest: # Pipeline to send data to Tracetest + receivers: [otlp] + processors: [batch] + exporters: [logging, otlp/tracetest] + traces/Dynatrace: # Pipeline to send data to Dynatrace + receivers: [otlp] + processors: [batch] + exporters: [logging, otlp/dynatrace] +``` + +## Configure Tracetest to Use Dynatrace as a Trace Data Store + +Configure your Tracetest instance to expose an `otlp` endpoint to make it aware it will receive traces from the OpenTelemetry Collector. This will expose Tracetest's trace receiver on port `4317`. + +## Connect Tracetest to Dynatrace with the Web UI + +In the Web UI, (1) open Settings, and, on the (2) Configure Data Store tab, select (3) Dynatrace. + + +![Dynatrace](../img/Dynatrace-settings.png) + +## Connect Tracetest to Dynatrace with the CLI + +Or, if you prefer using the CLI, you can use this file config. + +```yaml +type: DataStore +spec: + name: Dynatrace pipeline + type: dynatrace + default: true +``` + +Proceed to run this command in the terminal and specify the file above. + +```bash +tracetest apply datastore -f my/data-store/file/location.yaml +``` + + diff --git a/docs/docs/configuration/opentelemetry-collector-configuration-file-reference.md b/docs/docs/configuration/opentelemetry-collector-configuration-file-reference.md index 134efa670a..a67264b429 100644 --- a/docs/docs/configuration/opentelemetry-collector-configuration-file-reference.md +++ b/docs/docs/configuration/opentelemetry-collector-configuration-file-reference.md @@ -23,6 +23,7 @@ Currently, Tracetest supports the following data stores. Click on the respective - [Datadog](./connecting-to-data-stores/datadog) - [Honeycomb](./connecting-to-data-stores/honeycomb.md) - [Azure App Insights](./connecting-to-data-stores/azure-app-insights.md) +- [Dynatrace](./connecting-to-data-stores/dynatrace) Continue reading below to learn how to configure the OpenTelemetry Collector to send trace data from your application to any of the trace data stores above. diff --git a/docs/docs/configuration/overview.md b/docs/docs/configuration/overview.md index 6857199ae6..1787b5f0de 100644 --- a/docs/docs/configuration/overview.md +++ b/docs/docs/configuration/overview.md @@ -22,6 +22,7 @@ Currently, Tracetest supports the following data stores. Click on the respective - [Datadog](./connecting-to-data-stores/datadog) - [Honeycomb](./connecting-to-data-stores/honeycomb) - [Azure App Insights](./connecting-to-data-stores/azure-app-insights.md) +- [Dynatrace](./connecting-to-data-stores/dynatrace) ## Using Tracetest without a Trace Data Store diff --git a/docs/docs/examples-tutorials/img/dynatrace-distributed-traces-app.png b/docs/docs/examples-tutorials/img/dynatrace-distributed-traces-app.png new file mode 100644 index 0000000000..a5ad312af7 Binary files /dev/null and b/docs/docs/examples-tutorials/img/dynatrace-distributed-traces-app.png differ diff --git a/docs/docs/examples-tutorials/img/dynatrace-failed-test.png b/docs/docs/examples-tutorials/img/dynatrace-failed-test.png new file mode 100644 index 0000000000..bf53dfb832 Binary files /dev/null and b/docs/docs/examples-tutorials/img/dynatrace-failed-test.png differ diff --git a/docs/docs/examples-tutorials/img/dynatrace-successful-test.png b/docs/docs/examples-tutorials/img/dynatrace-successful-test.png new file mode 100644 index 0000000000..d291896947 Binary files /dev/null and b/docs/docs/examples-tutorials/img/dynatrace-successful-test.png differ diff --git a/docs/docs/examples-tutorials/img/dynatrace-trace-drilldown.png b/docs/docs/examples-tutorials/img/dynatrace-trace-drilldown.png new file mode 100644 index 0000000000..2c7ab9eb35 Binary files /dev/null and b/docs/docs/examples-tutorials/img/dynatrace-trace-drilldown.png differ diff --git a/docs/docs/examples-tutorials/recipes.md b/docs/docs/examples-tutorials/recipes.md index bd5fbba4ff..c743d2e5af 100644 --- a/docs/docs/examples-tutorials/recipes.md +++ b/docs/docs/examples-tutorials/recipes.md @@ -17,6 +17,7 @@ This integration point uses the OpenTelemetry Collector as a router to send trac - [Sending traces to New Relic and Tracetest from the OpenTelemetry Demo with OpenTelemetry Collector](./recipes/running-tracetest-with-new-relic.md) - [Sending traces to Datadog and Tracetest from the OpenTelemetry Demo with OpenTelemetry Collector](./recipes/running-tracetest-with-datadog.md) - [Sending traces to Honeycomb and Tracetest from a Node.js app using the OpenTelemetry Collector](./recipes/running-tracetest-with-honeycomb.md) +- [Sending traces to Dynatrace and Tracetest from the OpenTelemetry Demo with OpenTelemetry Collector](./recipes/running-tracetest-with-dynatrace.md) ### Jaeger diff --git a/docs/docs/examples-tutorials/recipes/running-tracetest-with-dynatrace.md b/docs/docs/examples-tutorials/recipes/running-tracetest-with-dynatrace.md new file mode 100644 index 0000000000..10e9c9215c --- /dev/null +++ b/docs/docs/examples-tutorials/recipes/running-tracetest-with-dynatrace.md @@ -0,0 +1,354 @@ +# Running Tracetest With Dynatrace + +:::note +[Check out the source code on GitHub here.](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-dynatrace) +::: + +[Tracetest](https://tracetest.io/) is a testing tool based on [OpenTelemetry](https://opentelemetry.io/) that allows you to test your distributed application. It allows you to use data from distributed traces generated by OpenTelemetry to validate and assert if your application has the desired behavior defined by your test definitions. + +[Dynatrace](https://www.dynatrace.com/) TODO. + +## OpenTelemetry Demo `v1.3.0` with Dynatrace, OpenTelemetry and Tracetest + +This is a simple sample app on how to configure the [OpenTelemetry Demo `v1.3.0`](https://github.com/open-telemetry/opentelemetry-demo) to use [Tracetest](https://tracetest.io/) for enhancing your E2E and integration tests with trace-based testing and [Dynatrace](https://www.dynatrace.com/) as a trace data store. + +## Prerequisites + +You will need [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed on your machine to run this sample app! Additionally, you will need a Dynatrace account and an API token. Sign up for a [free Dynatrace trial](https://www.dynatrace.com/trial). + +## Project Structure + +The project is built with Docker Compose. It contains two distinct `docker-compose.yaml` files. + +### 1. OpenTelemetry Demo + +The `docker-compose.yaml` file and `.env` file in the root directory are for the OpenTelemetry Demo. + +### 2. Tracetest + +The `docker-compose.yaml` file, `collector.config.yaml`, `tracetest-provision.yaml`, and `tracetest-config.yaml` in the `tracetest` directory are for setting up Tracetest and the OpenTelemetry Collector. + +The `tracetest` directory is self-contained and will run all the prerequisites for enabling OpenTelemetry traces and trace-based testing with Tracetest, as well as routing all traces the OpenTelemetry Demo generates to Lightstep. + +### Docker Compose Network + +All `services` in the `docker-compose.yaml` are on the same network, defined by the `networks` section on each file, and will be reachable by hostname from within other services. E.g. `tracetest:4317` in the `collector.config.yaml` will map to the `tracetest` service, where port `4317` is the port where Tracetest accepts traces. + + +## OpenTelemetry Demo + +The [OpenDelemetry Demo](https://github.com/open-telemetry/opentelemetry-demo) is a sample microservice-based app with the purpose to demo how to correctly set up OpenTelemetry distributed tracing. + +Read more about the OpenTelemetry Demo [here](https://opentelemetry.io/blog/2022/announcing-opentelemetry-demo-release/). + +The `docker-compose.yaml` contains 14 services. + +To start the OpenTelemetry Demo by itself, run this command: + +```bash +docker compose up +``` + +This will start the OpenTelemetry Demo. Open up `http://localhost:8084` to make sure it's working. But, you're not sending the traces anywhere. + +Let's fix this by configuring Tracetest and the OpenTelemetry Collector to forward trace data to both Dynatrace and Tracetest. + +## Tracetest + +The `docker-compose.yaml` in the `tracetest` directory is configured with three services. + +- **Postgres** - Postgres is a prerequisite for Tracetest to work. It stores trace data when running trace-based tests. +- [**OpenTelemetry Collector**](https://opentelemetry.io/docs/collector/) - A vendor-agnostic implementation of how to receive, process and export telemetry data. To support sending traces to Dynatrace, we are using the [`contrib` version](https://github.com/open-telemetry/opentelemetry-collector-contrib), which contains vendor-related code. +- [**Tracetest**](https://tracetest.io/) - Trace-based testing that generates end-to-end tests automatically from traces. + +```yaml +version: "3.9" + +networks: + default: + name: opentelemetry-demo + driver: bridge + +services: + tracetest: + restart: unless-stopped + image: kubeshop/tracetest:${TAG:-latest} + container_name: tracetest + platform: linux/amd64 + ports: + - 11633:11633 + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - type: bind + source: ./tracetest-config.yaml + target: /app/tracetest.yaml + - type: bind + source: ./tracetest-provision.yaml + target: /app/provisioning.yaml + command: --provisioning-file /app/provisioning.yaml + healthcheck: + test: ["CMD", "wget", "--spider", "localhost:11633"] + interval: 1s + timeout: 3s + retries: 60 + depends_on: + tt-postgres: + condition: service_healthy + otel-collector: + condition: service_started + environment: + TRACETEST_DEV: ${TRACETEST_DEV} + + tt-postgres: + image: postgres:14 + container_name: tt-postgres + environment: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + test: pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" + interval: 1s + timeout: 5s + retries: 60 + + otel-collector: + image: otel/opentelemetry-collector-contrib:0.68.0 + container_name: otel-collector + restart: unless-stopped + command: + - "--config" + - "/otel-local-config.yaml" + volumes: + - ./tracetest/collector.config.yaml:/otel-local-config.yaml + +``` + +Tracetest depends on both Postgres and the OpenTelemetry Collector. Both Tracetest and the OpenTelemetry Collector require config files to be loaded via a volume. The volumes are mapped from the root directory into the `tracetest` directory and the respective config files. + +To start both the OpenTelemetry Demo and Tracetest we will run this command: + +```bash +docker-compose -f docker-compose.yaml -f tracetest/docker-compose.yaml up +``` + +The `tracetest-config.yaml` file contains the basic setup of connecting Tracetest to the Postgres instance and telemetry exporter. The exporter is set to the OpenTelemetry Collector. + +```yaml +# tracetest-config.yaml + +--- +postgres: + host: postgres + user: postgres + password: postgres + port: 5432 + dbname: postgres + params: sslmode=disable + +telemetry: + exporters: + collector: + serviceName: tracetest + sampling: 100 + exporter: + type: collector + collector: + endpoint: otel-collector:4317 + +server: + telemetry: + exporter: collector + applicationExporter: collector +``` + +The `tracetest-provision.yaml` file contains the setup of the demo APIs that Tracetest can use as an example for tests, the polling profiles that say how Tracetest should fetch traces from the data store and the configuration for the data store, in our case, Dynatrace. + +```yaml +--- +type: Demo +spec: + name: "OpenTelemetry Shop" + enabled: true + type: otelstore + opentelemetryStore: + frontendEndpoint: http://frontend:8084 + productCatalogEndpoint: productcatalogservice:3550 + cartEndpoint: cartservice:7070 + checkoutEndpoint: checkoutservice:5050 + +--- +type: PollingProfile +spec: + name: Default + strategy: periodic + default: true + periodic: + retryDelay: 5s + timeout: 180s + +--- +type: DataStore +spec: + name: dynatrace + type: dynatrace + +``` + +**Sending Traces to Tracetest and Dynatrace** + +The `collector.config.yaml` explains that. It receives traces via either `grpc` or `http`. Then, exports them to Tracetest's OTLP endpoint `tracetest:4317` in one pipeline, and to Dynatrace in another. + +Make sure to add your Dynatrace API Key to the `otlp` exporter (needs the `openTelemetryTrace.ingest` permission). + +```yaml +receivers: + otlp: + protocols: + http: + grpc: + +processors: + batch: # this configuration is needed to guarantee that the data is sent correctly to Dynatrace + send_batch_max_size: 100 + send_batch_size: 10 + timeout: 10s + +exporters: + # OTLP for Tracetest + otlp/tracetest: + endpoint: tracetest:4317 + # Send traces to Tracetest. + # Read more in docs here: https://docs.tracetest.io/configuration/connecting-to-data-stores/opentelemetry-collector + tls: + insecure: true + # OTLP for Dynatrace + otlp/dynatrace: + endpoint: https://abc123.live.dynatrace.com/api/v2/otlp # Send traces to Dynatrace. Read more in docs here: https://www.dynatrace.com/support/help/extend-dynatrace/opentelemetry/collector#configuration + headers: + Authorization: "Api-Token dt0c01.sample.secret" # Requires "openTelemetryTrace.ingest" permission + +service: + pipelines: + traces/tracetest: + receivers: [otlp] + processors: [batch] + exporters: [otlp/tracetest] + traces/dynatrace: + receivers: [otlp] + processors: [batch] + exporters: [otlp/dynatrace] +``` + +## Run Both the OpenTelemetry Demo App and Tracetest + +To start both OpenTelemetry and Tracetest, run this command: + +```bash +docker-compose -f docker-compose.yaml -f tracetest/docker-compose.yaml up +``` + +This will start your Tracetest instance on `http://localhost:11633/`. + +Open the URL and [start creating tests in the Web UI](https://docs.tracetest.io/web-ui/creating-tests)! Make sure to use the endpoints within your Docker network like `http://frontend:8084/` when creating tests. + +This is because your OpenTelemetry Demo and Tracetest are in the same network. + +> Note: View the `demo` section in the `tracetest.config.yaml` to see which endpoints from the OpenTelemetry Demo are available for running tests. + +Here's a sample of a failed test run, which happens if you add this assertion: + +```css +attr:tracetest.span.duration < 10ms +``` + +![](../img/dynatrace-failed-test.png) + +Increasing the duration to a more reasonable `500ms` will make the test pass. + +![](../img/dynatrace-successful-test.png) + +## Run Tracetest Tests with the Tracetest CLI + +First, [install the CLI](https://docs.tracetest.io/getting-started/installation#install-the-tracetest-cli). +Then, configure the CLI: + +```bash +tracetest configure --endpoint http://localhost:11633 +``` + +Once configured, you can run a test against the Tracetest instance via the terminal. + +Check out the `http-test.yaml` file. + +```yaml +# http-test.yaml + +type: Test +spec: + id: JBYAfKJ4R + name: OpenTelemetry Shop - List Products + description: List Products available on OTel shop + trigger: + type: http + httpRequest: + url: http://frontend:8084/api/products + method: GET + headers: + - key: Content-Type + value: application/json + specs: + - selector: span[tracetest.span.type="general" name="Tracetest trigger"] + assertions: + - attr:tracetest.response.status = 200 + - attr:tracetest.span.duration < 10ms + - selector: span[tracetest.span.type="rpc" name="grpc.hipstershop.ProductCatalogService/ListProducts"] + assertions: + - attr:rpc.grpc.status_code = 0 + - selector: span[tracetest.span.type="rpc" name="hipstershop.ProductCatalogService/ListProducts" + rpc.system="grpc" rpc.method="ListProducts" rpc.service="hipstershop.ProductCatalogService"] + assertions: + - attr:rpc.grpc.status_code = 0 +``` + +This file defines a test the same way you would through the Web UI. + +To run the test, run this command in the terminal: + +```bash +tracetest run test -f ./http-test.yaml +``` + +This test will fail just like the sample above due to the `attr:tracetest.span.duration < 10ms` assertion. + +```bash +✘ OpenTelemetry Shop - List Products (http://localhost:11633/test/JBYAfKJ4R/run/3/test) + ✘ span[tracetest.span.type="general" name="Tracetest trigger"] + ✘ #2d1b0dcbd75b3a42 + ✔ attr:tracetest.response.status = 200 (200) + ✘ attr:tracetest.span.duration < 10ms (24ms) (http://localhost:11633/test/JBYAfKJ4R/run/3/test?selectedAssertion=0&selectedSpan=2d1b0dcbd75b3a42) + ✔ span[tracetest.span.type="rpc" name="grpc.hipstershop.ProductCatalogService/ListProducts"] + ✔ #90aeab1e9db4617b + ✔ attr:rpc.grpc.status_code = 0 (http://localhost:11633/test/JBYAfKJ4R/run/3/test?selectedAssertion=1) + ✔ span[tracetest.span.type="rpc" name="hipstershop.ProductCatalogService/ListProducts" rpc.system="grpc" rpc.method="ListProducts" rpc.service="hipstershop.ProductCatalogService"] + ✔ #44b836b092b4d708 + ✔ attr:rpc.grpc.status_code = 0 (http://localhost:11633/test/JBYAfKJ4R/run/3/test?selectedAssertion=2) +``` + +If you edit the duration as in the Web UI example above, the test will pass! + +## View Trace Spans Over Time in Dynatrace + +All distributed traces (whether generated by OpenTelemetry or the OneAgent) are available in Dynatrace in the distributed traces app. + +![](../img/dynatrace-distributed-traces-app.png) + +You can also drill down into a particular trace. + +![](../img/dynatrace-trace-drilldown.png) + +The combination of Dynatrace and Tracetest is extremely powerful. Ingest your traces to Dynatrace for long term storage at planetary scale and encourage your developers to incorporate trace-based testing into their workflows with Tracetest. + +## Learn more + +Feel free to check out our [examples in GitHub](https://github.com/kubeshop/tracetest/tree/main/examples) and join our [Discord Community](https://discord.gg/8MtcMrQNbX) for more info! diff --git a/docs/sidebars.js b/docs/sidebars.js index c521065728..35dd1ffd92 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -190,6 +190,11 @@ const sidebars = { id: "examples-tutorials/recipes/running-tracetest-with-datadog", label: "OpenTelemetry Demo and Datadog", }, + { + type: "doc", + id: "examples-tutorials/recipes/running-tracetest-with-dynatrace", + label: "OpenTelemetry Demo and Dynatrace", + }, { type: "doc", id: "examples-tutorials/recipes/running-tracetest-with-honeycomb", @@ -310,6 +315,11 @@ const sidebars = { id: "configuration/connecting-to-data-stores/datadog", label: "Datadog", }, + { + type: "doc", + id: "configuration/connecting-to-data-stores/dynatrace", + label: "Dynatrace", + }, { type: "doc", id: "configuration/connecting-to-data-stores/honeycomb", diff --git a/examples/tracetest-dynatrace/.gitignore b/examples/tracetest-dynatrace/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/tracetest-dynatrace/README.md b/examples/tracetest-dynatrace/README.md new file mode 100644 index 0000000000..4f14533660 --- /dev/null +++ b/examples/tracetest-dynatrace/README.md @@ -0,0 +1,15 @@ +# OpenTelemetry Demo with Tracetest and Dynatrace + +> [Read the detailed recipe for setting up Dynatrace with Tractest in our documentation.](https://docs.tracetest.io/examples-tutorials/recipes/running-tracetest-with-dynatrace) + +This example uses the OpenTelemetry Demo `v1.3.0`. + +This is a simple sample app on how to configure the [OpenTelemetry Demo](https://github.com/open-telemetry/opentelemetry-demo) to use [Tracetest](https://tracetest.io/) for enhancing your E2E and integration tests with trace-based testing, and [Dynatrace](https://www.dynatrace.com/) as a trace data store. + +Feel free to check out the [docs](https://docs.tracetest.io/), and join our [Discord Community](https://discord.gg/8MtcMrQNbX) for more info! + +You can run it locally using the command: + +```sh +docker compose -f ./docker-compose.yaml -f ./tracetest/docker-compose.yaml up +``` \ No newline at end of file diff --git a/examples/tracetest-dynatrace/docker-compose.yml b/examples/tracetest-dynatrace/docker-compose.yml new file mode 100644 index 0000000000..37b6a37754 --- /dev/null +++ b/examples/tracetest-dynatrace/docker-compose.yml @@ -0,0 +1,50 @@ +version: '3' +services: + tracetest: + image: kubeshop/tracetest:${TAG:-latest} + platform: linux/amd64 + volumes: + - type: bind + source: ./tracetest/tracetest-config.yaml + target: /app/tracetest.yaml + - type: bind + source: ./tracetest/tracetest-provision.yaml + target: /app/provision.yaml + command: --provisioning-file /app/provision.yaml + ports: + - 11633:11633 + extra_hosts: + - "host.docker.internal:host-gateway" + depends_on: + postgres: + condition: service_healthy + otel-collector: + condition: service_started + healthcheck: + test: [ "CMD", "wget", "--spider", "localhost:11633" ] + interval: 1s + timeout: 3s + retries: 60 + environment: + TRACETEST_DEV: ${TRACETEST_DEV} + + postgres: + image: postgres:14 + environment: + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + healthcheck: + test: pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" + interval: 1s + timeout: 5s + retries: 60 + + otel-collector: + image: otel/opentelemetry-collector:0.54.0 + command: + - "--config" + - "/otel-local-config.yaml" + volumes: + - ./tracetest/collector.config.yaml:/otel-local-config.yaml + ports: + - 4317:4317 \ No newline at end of file diff --git a/examples/tracetest-dynatrace/tests/list-tests.yaml b/examples/tracetest-dynatrace/tests/list-tests.yaml new file mode 100644 index 0000000000..294b8af9fe --- /dev/null +++ b/examples/tracetest-dynatrace/tests/list-tests.yaml @@ -0,0 +1,21 @@ +type: Test +spec: + id: e9c6cff9-974d-4263-8a23-22f1e9f975aa + name: List all tracetest tests + description: List all existing tests from tracetest API + trigger: + type: http + httpRequest: + url: http://localhost:11633/api/tests + method: GET + headers: + - key: Content-Type + value: application/json + grpc: + protobufFile: "" + address: "" + method: "" + specs: + - selector: span[tracetest.span.type="http" name="GET /api/tests"] + assertions: + - attr:tracetest.selected_spans.count = 1 diff --git a/examples/tracetest-dynatrace/tracetest/collector.config.yaml b/examples/tracetest-dynatrace/tracetest/collector.config.yaml new file mode 100644 index 0000000000..36e5d7adba --- /dev/null +++ b/examples/tracetest-dynatrace/tracetest/collector.config.yaml @@ -0,0 +1,34 @@ +receivers: + otlp: + protocols: + grpc: + http: + +processors: + batch: + timeout: 100ms + + # Data sources: traces + probabilistic_sampler: + hash_seed: 22 + sampling_percentage: 100 + +exporters: + # OTLP for Tracetest + otlp/tracetest: + endpoint: tracetest:4317 # Send traces to Tracetest. + # Read more in docs here: https://docs.tracetest.io/configuration/connecting-to-data-stores/opentelemetry-collector + tls: + insecure: true + # OTLP for Dynatrace + otlp/dynatrace: + endpoint: https://abc12345.live.dynatrace.com/api/v2/otlp + headers: + Authorization: "Api-Token dt0c01.sample.secret" # Requires "openTelemetryTrace.ingest" permission + +service: + pipelines: + traces: + receivers: [otlp] + processors: [probabilistic_sampler, batch] + exporters: [otlp/dynatrace,otlp/tracetest] diff --git a/examples/tracetest-dynatrace/tracetest/tracetest-config.yaml b/examples/tracetest-dynatrace/tracetest/tracetest-config.yaml new file mode 100644 index 0000000000..cbe3226feb --- /dev/null +++ b/examples/tracetest-dynatrace/tracetest/tracetest-config.yaml @@ -0,0 +1,21 @@ +postgres: + host: postgres + user: postgres + password: postgres + port: 5432 + dbname: postgres + params: sslmode=disable + +telemetry: + exporters: + collector: + serviceName: tracetest + sampling: 100 # 100% + exporter: + type: collector + collector: + endpoint: otel-collector:4317 + +server: + telemetry: + exporter: collector diff --git a/examples/tracetest-dynatrace/tracetest/tracetest-provision.yaml b/examples/tracetest-dynatrace/tracetest/tracetest-provision.yaml new file mode 100644 index 0000000000..95b99d5821 --- /dev/null +++ b/examples/tracetest-dynatrace/tracetest/tracetest-provision.yaml @@ -0,0 +1,23 @@ +--- +type: PollingProfile +spec: + name: Default + strategy: periodic + default: true + periodic: + retryDelay: 5s + timeout: 10m + +--- +type: DataStore +spec: + name: Dynatrace + type: dynatrace +--- +type: TestRunner +spec: + id: current + name: default + requiredGates: + - analyzer-score + - test-specs diff --git a/server/datastore/datastore_entities.go b/server/datastore/datastore_entities.go index 8fd903d5a1..3327700835 100644 --- a/server/datastore/datastore_entities.go +++ b/server/datastore/datastore_entities.go @@ -140,6 +140,7 @@ const ( DataStoreTypeHoneycomb DataStoreType = "honeycomb" DatastoreTypeAzureAppInsights DataStoreType = "azureappinsights" DatastoreTypeSignoz DataStoreType = "signoz" + DatastoreTypeDynatrace DataStoreType = "dynatrace" ) var validTypes = []DataStoreType{ @@ -156,6 +157,7 @@ var validTypes = []DataStoreType{ DataStoreTypeHoneycomb, DatastoreTypeAzureAppInsights, DatastoreTypeSignoz, + DatastoreTypeDynatrace, } var otlpBasedDataStores = []DataStoreType{ @@ -165,6 +167,7 @@ var otlpBasedDataStores = []DataStoreType{ DataStoreTypeDataDog, DataStoreTypeHoneycomb, DatastoreTypeSignoz, + DatastoreTypeDynatrace, } func (ds DataStore) Validate() error { diff --git a/server/http/mappings/datastore.go b/server/http/mappings/datastore.go index b24dd937d6..08029cbe93 100644 --- a/server/http/mappings/datastore.go +++ b/server/http/mappings/datastore.go @@ -59,6 +59,7 @@ var dataStoreTypesMapping = map[datastore.DataStoreType]openapi.SupportedDataSto datastore.DataStoreTypeAwsXRay: openapi.AWSXRAY, datastore.DataStoreTypeHoneycomb: openapi.HONEYCOMB, datastore.DatastoreTypeAzureAppInsights: openapi.AZUREAPPINSIGHTS, + datastore.DatastoreTypeDynatrace: openapi.DYNATRACE, } func (m OpenAPI) DataStoreType(in datastore.DataStoreType) openapi.SupportedDataStores { diff --git a/server/openapi/model_supported_data_stores.go b/server/openapi/model_supported_data_stores.go index 7e154cb471..20423930e3 100644 --- a/server/openapi/model_supported_data_stores.go +++ b/server/openapi/model_supported_data_stores.go @@ -26,6 +26,7 @@ const ( HONEYCOMB SupportedDataStores = "honeycomb" AZUREAPPINSIGHTS SupportedDataStores = "azureappinsights" SIGNOZ SupportedDataStores = "signoz" + DYNATRACE SupportedDataStores = "dynatrace" ) // AssertSupportedDataStoresRequired checks if the required fields are not zero-ed diff --git a/server/tracedb/tracedb_test.go b/server/tracedb/tracedb_test.go index 3813e5ef26..bb31ccbfd7 100644 --- a/server/tracedb/tracedb_test.go +++ b/server/tracedb/tracedb_test.go @@ -119,6 +119,14 @@ func TestCreateClient(t *testing.T) { }, expectedType: "*tracedb.OTLPTraceDB", }, + { + name: "Dynatrace", + ds: datastore.DataStore{ + Type: datastore.DatastoreTypeDynatrace, + Values: datastore.DataStoreValues{}, + }, + expectedType: "*tracedb.OTLPTraceDB", + }, { name: "EmptyConfig", ds: datastore.DataStore{}, diff --git a/testing/cli-e2etest/README.md b/testing/cli-e2etest/README.md index 28d6b992c5..facdbcf921 100644 --- a/testing/cli-e2etest/README.md +++ b/testing/cli-e2etest/README.md @@ -11,9 +11,9 @@ The main idea is to test every CLI command against the Tracetest server with dif ## Tracetest Data Store -| Jaeger | Tempo | OpenSearch | SignalFx | OTLP | ElasticAPM | New Relic | Lightstep | Datadog | AWS X-Ray | Honeycomb | -| ------------------ | ------ | ---------- | -------- | ------ | ---------- | --------- | --------- | ------- | --------- | --------- | -| :white_check_mark: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | +| Jaeger | Tempo | OpenSearch | SignalFx | OTLP | ElasticAPM | New Relic | Lightstep | Datadog | AWS X-Ray | Honeycomb | Dynatrace | +| ------------------ | ------ | ---------- | -------- | ------ | ---------- | --------- | --------- | ------- | --------- | --------- | --------- | +| :white_check_mark: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | :soon: | ## CLI Commands to Test diff --git a/web/src/components/DataStoreIcon/DataStoreIcon.tsx b/web/src/components/DataStoreIcon/DataStoreIcon.tsx index d98b012eff..3fc2b92afa 100644 --- a/web/src/components/DataStoreIcon/DataStoreIcon.tsx +++ b/web/src/components/DataStoreIcon/DataStoreIcon.tsx @@ -13,6 +13,7 @@ import AWSXRay from './Icons/AwsXRay'; import Honeycomb from './Icons/Honeycomb'; import AzureAppInsights from './Icons/AzureAppInsights'; import Signoz from './Icons/Signoz'; +import Dynatrace from './Icons/Dynatrace'; const iconMap = { [SupportedDataStores.JAEGER]: Jaeger, @@ -28,6 +29,7 @@ const iconMap = { [SupportedDataStores.Honeycomb]: Honeycomb, [SupportedDataStores.AzureAppInsights]: AzureAppInsights, [SupportedDataStores.Signoz]: Signoz, + [SupportedDataStores.Dynatrace]: Dynatrace, } as const; interface IProps { diff --git a/web/src/components/DataStoreIcon/Icons/Dynatrace.tsx b/web/src/components/DataStoreIcon/Icons/Dynatrace.tsx new file mode 100644 index 0000000000..cb8f8712bd --- /dev/null +++ b/web/src/components/DataStoreIcon/Icons/Dynatrace.tsx @@ -0,0 +1,26 @@ +import {IIconProps} from '../DataStoreIcon'; + +const Dynatrace = ({color, width = '20', height = '20'}: IIconProps) => { + return ( + + + + + + + ); +}; + +export default Dynatrace; \ No newline at end of file diff --git a/web/src/components/Settings/DataStorePlugin/DataStorePlugin.tsx b/web/src/components/Settings/DataStorePlugin/DataStorePlugin.tsx index a6242bba7e..04443dc97f 100644 --- a/web/src/components/Settings/DataStorePlugin/DataStorePlugin.tsx +++ b/web/src/components/Settings/DataStorePlugin/DataStorePlugin.tsx @@ -21,6 +21,7 @@ export const DataStoreComponentMap: IDataStorePluginMap = { [SupportedDataStores.AWSXRay]: AwsXRay, [SupportedDataStores.AzureAppInsights]: AzureAppInsights, [SupportedDataStores.Signoz]: OpenTelemetryCollector, + [SupportedDataStores.Dynatrace]: OpenTelemetryCollector, }; export default DataStoreComponentMap; diff --git a/web/src/constants/CollectorConfig.constants.ts b/web/src/constants/CollectorConfig.constants.ts index c1ce0d256e..5185d51b64 100644 --- a/web/src/constants/CollectorConfig.constants.ts +++ b/web/src/constants/CollectorConfig.constants.ts @@ -235,6 +235,42 @@ service: exporters: [logging, otlp/signoz] `; +export const Dynatrace = `receivers: + otlp: + protocols: + grpc: + http: + +processors: + batch: + timeout: 100ms + +exporters: + logging: + verbosity: detailed + # OTLP for Tracetest + otlp/tracetest: + endpoint: tracetest:4317 # Send traces to Tracetest. Read more in docs here: https://docs.tracetest.io/configuration/connecting-to-data-stores/opentelemetry-collector + tls: + insecure: true + # OTLP for Dynatrace + otlp/dynatrace: + endpoint: https://abc12345.live.dynatrace.com/api/v2/otlp # Send traces to Dynatrace. Read more in docs here: https://www.dynatrace.com/support/help/extend-dynatrace/opentelemetry/collector#configuration + headers: + Authorization: "Api-Token dt0c01.sample.secret" # Requires "openTelemetryTrace.ingest" permission + +service: + pipelines: + traces/tracetest: + receivers: [otlp] + processors: [batch] + exporters: [logging, otlp/tracetest] + traces/dynatrace: + receivers: [otlp] + processors: [batch] + exporters: [logging, otlp/dynatrace] +`; + export const CollectorConfigMap = { [SupportedDataStores.Datadog]: Datadog, [SupportedDataStores.Lightstep]: Lightstep, @@ -243,4 +279,5 @@ export const CollectorConfigMap = { [SupportedDataStores.Honeycomb]: Honeycomb, [SupportedDataStores.AzureAppInsights]: AzureAppInsights, [SupportedDataStores.Signoz]: Signoz, + [SupportedDataStores.Dynatrace]: Dynatrace, } as const; diff --git a/web/src/constants/DataStore.constants.tsx b/web/src/constants/DataStore.constants.tsx index 1590d72b62..1a84e01268 100644 --- a/web/src/constants/DataStore.constants.tsx +++ b/web/src/constants/DataStore.constants.tsx @@ -14,6 +14,7 @@ export const SupportedDataStoresToName = { [SupportedDataStores.Honeycomb]: 'Honeycomb', [SupportedDataStores.AzureAppInsights]: 'Azure App Insights', [SupportedDataStores.Signoz]: 'Signoz', + [SupportedDataStores.Dynatrace]: 'Dynatrace', } as const; export const SupportedDataStoresToDocsLink = { @@ -33,6 +34,8 @@ export const SupportedDataStoresToDocsLink = { 'https://docs.tracetest.io/configuration/connecting-to-data-stores/azure-app-insights', [SupportedDataStores.Signoz]: 'https://docs.tracetest.io/configuration/connecting-to-data-stores/signoz', + [SupportedDataStores.Dynatrace]: + 'https://docs.tracetest.io/configuration/connecting-to-data-stores/dynatrace', } as const; export const SupportedDataStoresToDefaultEndpoint = { @@ -49,4 +52,5 @@ export const SupportedDataStoresToDefaultEndpoint = { [SupportedDataStores.Honeycomb]: '', [SupportedDataStores.AzureAppInsights]: '', [SupportedDataStores.Signoz]: '', + [SupportedDataStores.Dynatrace]: 'https://abc12345.live.dynatrace.com/api/v2/otlp', } as const; diff --git a/web/src/models/DataStore.model.ts b/web/src/models/DataStore.model.ts index 8c33c8e315..aa42418b4a 100644 --- a/web/src/models/DataStore.model.ts +++ b/web/src/models/DataStore.model.ts @@ -13,6 +13,7 @@ type DataStore = Model['spec'] & { honeycomb?: TRawOtlpDataStore; azureappinsights?: TRawAzureAppInsightsDataStore & TRawOtlpDataStore; signoz?: TRawOtlpDataStore; + dynatrace?: TRawOtlpDataStore; }; const DataStore = ({ diff --git a/web/src/services/DataStore.service.ts b/web/src/services/DataStore.service.ts index 5887329690..f52635855b 100644 --- a/web/src/services/DataStore.service.ts +++ b/web/src/services/DataStore.service.ts @@ -23,6 +23,7 @@ const dataStoreServiceMap = { [SupportedDataStores.AWSXRay]: AwsXRayService, [SupportedDataStores.AzureAppInsights]: AzureAppInsightsService, [SupportedDataStores.Signoz]: OtelCollectorService, + [SupportedDataStores.Dynatrace]: OtelCollectorService, } as const; interface IDataStoreService { diff --git a/web/src/types/DataStore.types.ts b/web/src/types/DataStore.types.ts index 7c706a8083..a27d8b4e3a 100644 --- a/web/src/types/DataStore.types.ts +++ b/web/src/types/DataStore.types.ts @@ -29,6 +29,7 @@ export enum SupportedDataStores { Honeycomb = 'honeycomb', AzureAppInsights = 'azureappinsights', Signoz = 'signoz', + Dynatrace = 'dynatrace' } export enum SupportedClientTypes { @@ -41,7 +42,8 @@ export type TCollectorDataStores = | SupportedDataStores.OtelCollector | SupportedDataStores.Lightstep | SupportedDataStores.Datadog - | SupportedDataStores.Signoz; + | SupportedDataStores.Signoz + | SupportedDataStores.Dynatrace; export type TRawGRPCClientSettings = TDataStoreSchemas['GRPCClientSettings']; export type TRawElasticSearch = TDataStoreSchemas['ElasticSearch']; @@ -96,6 +98,7 @@ export type IDataStore = DataStore & { datadog?: TRawOtlpDataStore; honeycomb?: TRawOtlpDataStore; signoz?: TRawOtlpDataStore; + dynatrace?: TRawOtlpDataStore; }; export type TDraftDataStore = {