From 6e2fda85ea690dfe088cae0ca2cb528f54220cb5 Mon Sep 17 00:00:00 2001 From: Adnan Rahic Date: Mon, 29 Jul 2024 14:53:45 +0200 Subject: [PATCH] docs(examples+recipe): add pw synth mon recipe --- docs/docs/examples-tutorials/recipes.mdx | 1 + ...tests-with-tracetest-playwright-engine.mdx | 3 +- ...onitoring-trace-based-playwright-tests.mdx | 333 ++++++++++++++++++ docs/sidebars.js | 5 + .../readme.md | 2 +- .../workflows/synthetic-monitoring.yaml | 0 .../Dockerfile | 12 + .../docker-compose.yaml | 12 + .../go.mod | 0 .../go.sum | 0 .../main.go | 0 .../readme.md | 12 + .../test-api.yaml | 0 .../tracetest/collector.config.yaml | 0 .../tracetest/docker-compose.yaml | 0 .../tracetest/tracetest-provision.yaml | 0 .../tracetest/tracetest.config.yaml | 0 .../.env.template | 2 + .../tracetest-synthetic-monitoring/.gitignore | 13 + .../tracetest-synthetic-monitoring/Dockerfile | 21 +- .../collector.config.yaml | 27 ++ .../docker-compose.yaml | 149 +++++++- .../tracetest-synthetic-monitoring/readme.md | 19 +- .../resources/apply.sh | 19 + .../resources/datastore.yaml | 12 + .../resources/import-pokemon.yaml | 31 ++ .../resources/monitor.yaml | 24 ++ .../resources/script.js | 36 ++ 28 files changed, 705 insertions(+), 28 deletions(-) create mode 100644 docs/docs/examples-tutorials/recipes/synthetic-monitoring-trace-based-playwright-tests.mdx rename examples/{tracetest-synthetic-monitoring => tracetest-synthetic-monitoring-github-actions}/.github/workflows/synthetic-monitoring.yaml (100%) create mode 100644 examples/tracetest-synthetic-monitoring-github-actions/Dockerfile create mode 100644 examples/tracetest-synthetic-monitoring-github-actions/docker-compose.yaml rename examples/{tracetest-synthetic-monitoring => tracetest-synthetic-monitoring-github-actions}/go.mod (100%) rename examples/{tracetest-synthetic-monitoring => tracetest-synthetic-monitoring-github-actions}/go.sum (100%) rename examples/{tracetest-synthetic-monitoring => tracetest-synthetic-monitoring-github-actions}/main.go (100%) create mode 100644 examples/tracetest-synthetic-monitoring-github-actions/readme.md rename examples/{tracetest-synthetic-monitoring => tracetest-synthetic-monitoring-github-actions}/test-api.yaml (100%) rename examples/{tracetest-synthetic-monitoring => tracetest-synthetic-monitoring-github-actions}/tracetest/collector.config.yaml (100%) rename examples/{tracetest-synthetic-monitoring => tracetest-synthetic-monitoring-github-actions}/tracetest/docker-compose.yaml (100%) rename examples/{tracetest-synthetic-monitoring => tracetest-synthetic-monitoring-github-actions}/tracetest/tracetest-provision.yaml (100%) rename examples/{tracetest-synthetic-monitoring => tracetest-synthetic-monitoring-github-actions}/tracetest/tracetest.config.yaml (100%) create mode 100644 examples/tracetest-synthetic-monitoring/.env.template create mode 100644 examples/tracetest-synthetic-monitoring/.gitignore create mode 100644 examples/tracetest-synthetic-monitoring/collector.config.yaml create mode 100644 examples/tracetest-synthetic-monitoring/resources/apply.sh create mode 100644 examples/tracetest-synthetic-monitoring/resources/datastore.yaml create mode 100644 examples/tracetest-synthetic-monitoring/resources/import-pokemon.yaml create mode 100644 examples/tracetest-synthetic-monitoring/resources/monitor.yaml create mode 100644 examples/tracetest-synthetic-monitoring/resources/script.js diff --git a/docs/docs/examples-tutorials/recipes.mdx b/docs/docs/examples-tutorials/recipes.mdx index c1827b6735..1acb75ad18 100644 --- a/docs/docs/examples-tutorials/recipes.mdx +++ b/docs/docs/examples-tutorials/recipes.mdx @@ -29,6 +29,7 @@ These guides show how to get the best of your instrumentation using the native T These guides show how to get the best trace-based testing with Synthetic Monitoring. - [Synthetic Monitoring with Trace-based API Tests](/examples-tutorials/recipes/synthetic-monitoring-trace-based-api-tests) +- [Synthetic Monitoring with Trace-based Playwright Tests](/examples-tutorials/recipes/synthetic-monitoring-trace-based-playwright-tests) ## Automation & Provisioning diff --git a/docs/docs/examples-tutorials/recipes/running-tests-with-tracetest-playwright-engine.mdx b/docs/docs/examples-tutorials/recipes/running-tests-with-tracetest-playwright-engine.mdx index 7ed3afb021..50336f8034 100644 --- a/docs/docs/examples-tutorials/recipes/running-tests-with-tracetest-playwright-engine.mdx +++ b/docs/docs/examples-tutorials/recipes/running-tests-with-tracetest-playwright-engine.mdx @@ -1,7 +1,7 @@ --- id: running-tests-with-tracetest-playwright-engine title: True End-To-End Trace-Based Tests with the Tracetest Playwright Engine Trigger -description: Quickstart on how to crete True End-To-End Trace-Based Tests with the Tracetest Playwright Engine Trigger +description: Quickstart on how to create True End-To-End Trace-Based Tests with the Tracetest Playwright Engine Trigger hide_table_of_contents: false keywords: - tracetest @@ -53,7 +53,6 @@ Another big benefit of using traces as test specs is that you can: ## Run This Example The example below is provided as part of the Tracetest GitHub repo. You can download and run the example by following these steps: -Clone the Tracetest project and go to the Privisioning Developer Environment with CLI example directory: ```bash git clone https://github.com/kubeshop/tracetest diff --git a/docs/docs/examples-tutorials/recipes/synthetic-monitoring-trace-based-playwright-tests.mdx b/docs/docs/examples-tutorials/recipes/synthetic-monitoring-trace-based-playwright-tests.mdx new file mode 100644 index 0000000000..d2f36fd567 --- /dev/null +++ b/docs/docs/examples-tutorials/recipes/synthetic-monitoring-trace-based-playwright-tests.mdx @@ -0,0 +1,333 @@ +--- +id: synthetic-monitoring-trace-based-playwright-tests +title: Synthetic Monitoring with Trace-based Playwright Tests +description: Quickstart on how to create True End-To-End Trace-Based Tests with the Tracetest Playwright Engine Trigger and run them as Synthetic Monitors. +hide_table_of_contents: false +keywords: + - tracetest + - trace-based testing + - observability + - distributed tracing + - end-to-end testing + - tracetest + - playwright + - trace-based-testing + - synthetic monitoring + - synthetic testing +image: https://res.cloudinary.com/djwdcmwdz/image/upload/v1698686403/docs/Blog_Thumbnail_14_rsvkmo.jpg +--- + +:::info Version Compatibility +The features described here are compatible with the [Tracetest CLI v1.4.1](https://github.com/kubeshop/tracetest/releases/tag/v1.4.1) and above. +::: + +:::note +[Check out the source code on GitHub here.](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-synthetic-monitoring) +::: + +[Tracetest](https://tracetest.io/) is a synthetic monitoring and testing tool based on [OpenTelemetry](https://opentelemetry.io/) that allows you to test distributed apps. You can use data from distributed traces generated by OpenTelemetry to validate and assert the functionality of your apps. + +[Playwright](https://playwright.dev/) is an open-source automation framework developed by Microsoft that enables cross-browser automation for web applications. It provides a set of APIs and libraries for automating interactions with web browsers such as Chrome, Firefox, and Microsoft Edge. + +## Why is this important? + +This recipe uses the Tracetest Playwright Engine trigger with Tracetest Monitors for synthetic monitoring. + +With these two working together you can combine the power of end-to-end tests with trace-based testing to easily capture a full distributed trace from your OpenTelemetry instrumented front-end and back-end system, but also run them on a schedule with enabled alerting when tests fail. + +Benefits of using traces as test specs alongside synthetic monitoring: + +- Get faster MTTR for failing performance tests +- Assert against the Playwright test execution and the system under test +- Validate functionality of other parts of your system that may be broken, even when end-to-end tests are passing +- Create synthetic tests that run in defined intervals +- Get alerted when synthetic tests fail via a Webhook + +## Requirements + +**Tracetest Account**: + +- Sign up to [`app.tracetest.io`](https://app.tracetest.io) or follow the [get started](/getting-started/installation) docs. +- Create an [environment](/concepts/environments). +- Create an [environment token](/concepts/environment-tokens) with **admin role**. +- Copy the environment id to your clipboard. + +**Docker**: Have [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed on your machine. + +## Run This Example + +:::info View Playwright recipe +This recipe uses the [official Playwright Engine recipe](/examples-tutorials/recipes/running-tests-with-tracetest-playwright-engine). +::: + +The example below is provided as part of the Tracetest GitHub repo. You can download and run the example by following these steps: + +```bash +git clone https://github.com/kubeshop/tracetest +cd tracetest/examples/tracetest-synthetic-monitoring +``` + +Follow these instructions to run the example: + +1. Log into the [Tracetest app](https://app.tracetest.io/). +2. Copy the `.env.template` file to `.env`. +3. Fill out the [TRACETEST_API_TOKEN](/concepts/environment-tokens) and [TRACETEST_ENVIRONMENT_ID](/concepts/agent) details by editing your `.env` file. You can find these values in the Settings area for your environment. +4. Run `docker compose run tracetest-apply`. +5. Follow the link in the terminal with the results to view the Monitor run. + +## Project Structure + +The project structure for running Tracetest Playwright Engine tests is as follows: + +```bash +.env.template +.gitignore +.Dockerfile +collector.config.yaml +docker-compose.yaml +/resources + apply.sh + datastore.yaml + import-pokemon.yaml + script.js +``` + +The [Pokeshop Demo App](/live-examples/pokeshop/overview) is a complete example of a distributed application using different back-end and front-end services. We will be launching it and running tests against it as part of this example. +The `docker-compose.yaml` file in the root directory of the quick start runs the Pokeshop Demo app, the OpenTelemetry Collector, Jaeger, and the [Tracetest Agent](/concepts/agent) setup. + +The Tracetest resource definitions and scripts are defined under the `/resources` directory. The resources include tests and the tracing backend definition, while the scripts include the `apply.sh` script to apply the resources. + +## Provisioned Resources + +The example provisions the following resources: + +### Import Pokemon Test + +```yaml title="resources/import-pokemon.yaml" +type: Test +spec: + id: import-pokemon + name: Import Pokemon + trigger: + type: playwrightengine + playwrightEngine: + target: http://api:8081 + script: ./script.js + method: importPokemon + specs: + - selector: span[tracetest.span.type="general" name="documentLoad"] + name: Document Load Should be fast + assertions: + - attr:tracetest.span.duration < 500ms + - selector: span[tracetest.span.type="http" http.scheme="http"] + name: All HTTP request should return 200 + assertions: + - attr:http.status_code = 200 + - selector: span[tracetest.span.type="messaging" name="queue.synchronizePokemon + process" messaging.system="rabbitmq" + messaging.destination="queue.synchronizePokemon" + messaging.operation="process"] + name: The worker should be processed + assertions: + - attr:tracetest.selected_spans.count = 1 + - selector: span[tracetest.span.type="database"] + name: "All Database Spans: Processing time is less than 100ms" + assertions: + - attr:tracetest.span.duration < 250ms +``` + +### Playwright Script + +```javascript title="resources/script.js" +const { expect } = require("@playwright/test"); + +async function importPokemon(page) { + expect(await page.getByText("Pokeshop")).toBeTruthy(); + + await page.click("text=Import"); + await page.getByLabel("ID").fill("143"); + + await Promise.all([ + page.waitForResponse((resp) => resp.url().includes("/pokemon/import") && resp.status() === 200), + page.getByRole("button", { name: "OK", exact: true }).click(), + ]); +} + +module.exports = { importPokemon }; +``` + +### Jaeger Tracing Backend + +```yaml title="resources/datastore.yaml" +type: DataStore +spec: + id: current + name: jaeger + type: jaeger + default: true + jaeger: + endpoint: jaeger:16685 + headers: + "": "" + tls: + insecure: true +``` + +### The Apply Script + +The apply script configures and provisions the resources in the Tracetest environment: + +```bash title="resources/apply.sh" +#!/bin/sh + +set -e + +TOKEN=$TRACETEST_API_KEY +ENVIRONMENT_ID=$TRACETEST_ENVIRONMENT_ID + +apply() { + echo "Configuring Tracetest" + tracetest configure --token $TOKEN --environment $ENVIRONMENT_ID + + echo "Applying Resources" + tracetest apply datastore -f /resources/datastore.yaml + tracetest apply test -f /resources/import-pokemon.yaml + tracetest apply monitor -f /resources/monitor.yaml + tracetest list monitor +} + +apply +``` + +## Setting the Environment Variables + +Copy the `.env.template` file to `.env` and add the Tracetest API token and environment id to the `TRACETEST_API_TOKEN` and `TRACETEST_ENVIRONMENT_ID` variables. + +## Running the Full Example + +Creating the resources is automated for you. You only need to run the following command: + +```bash +docker compose run tracetest-apply +``` + +This command will run the `apply.sh` script to provision the resources and create a synthetic monitor. + +## Viewing the Created Resources + +The output from the Tracetest resource apply script should be visible in the console log after running the `apply` command. + +```bash title="Output" +[+] Running 2/2 + ✔ api Pulled 2.4s + ✔ worker Pulled 2.6s +[+] Creating 8/0 + ✔ Container tracetest-synthetic-monitoring-playwright-engine-queue-1 Running 0.0s + ✔ Container tracetest-synthetic-monitoring-playwright-engine-postgres-1 Running 0.0s + ✔ Container tracetest-synthetic-monitoring-playwright-engine-cache-1 Running 0.0s + ✔ Container tracetest-synthetic-monitoring-playwright-engine-otel-collector-1 Running 0.0s + ✔ Container tracetest-synthetic-monitoring-playwright-engine-jaeger-1 Running 0.0s + ✔ Container tracetest-synthetic-monitoring-playwright-engine-tracetest-agent-1 Running 0.0s + ✔ Container tracetest-synthetic-monitoring-playwright-engine-worker-1 Running 0.0s + ✔ Container tracetest-synthetic-monitoring-playwright-engine-api-1 Running 0.0s +[+] Running 3/3 + ✔ Container tracetest-synthetic-monitoring-playwright-engine-queue-1 Hea... 1.0s + ✔ Container tracetest-synthetic-monitoring-playwright-engine-postgres-1 Healthy 1.0s + ✔ Container tracetest-synthetic-monitoring-playwright-engine-cache-1 Hea... 1.0s +[+] Running 2/2 + ✔ api Pulled 1.8s + ✔ worker Pulled 1.9s +Configuring Tracetest + SUCCESS Successfully configured Tracetest CLI +Applying Resources +type: DataStore +spec: + id: current + name: jaeger + type: jaeger + default: true + createdAt: 2024-05-13T14:22:27.325353Z + jaeger: + endpoint: jaeger:16685 + headers: + "": "" + tls: + insecure: true + +type: Test +spec: + id: import-pokemon + name: Import Pokemon + trigger: + type: playwrightengine + playwrightEngine: + target: http://api:8081 + script: "const { expect } = require(\"@playwright/test\");\n\nasync function addPokemon(page) {\n expect(await page.getByText(\"Pokeshop\")).toBeTruthy();\n\n await page.click(\"text=Add\");\n\n await page.getByLabel(\"Name\").fill(\"Charizard\");\n await page.getByLabel(\"Type\").fill(\"Flying\");\n await page\n .getByLabel(\"Image URL\")\n .fill(\"https://upload.wikimedia.org/wikipedia/en/1/1f/Pok%C3%A9mon_Charizard_art.png\");\n await page.getByRole(\"button\", { name: \"OK\", exact: true }).click();\n}\n\nasync function deletePokemon(page) {\n expect(await page.getByText(\"Pokeshop\")).toBeTruthy();\n\n await page.locator('[data-cy=\"pokemon-list\"]');\n await page.locator('[data-cy=\"pokemon-card\"]').first().click();\n await page.locator('[data-cy=\"pokemon-card\"] [data-cy=\"delete-pokemon-button\"]').first().click();\n}\n\nasync function importPokemon(page) {\n expect(await page.getByText(\"Pokeshop\")).toBeTruthy();\n\n await page.click(\"text=Import\");\n await page.getByLabel(\"ID\").fill(\"143\");\n\n await Promise.all([\n page.waitForResponse((resp) => resp.url().includes(\"/pokemon/import\") && resp.status() === 200),\n page.getByRole(\"button\", { name: \"OK\", exact: true }).click(),\n ]);\n}\n\nmodule.exports = { addPokemon, deletePokemon, importPokemon };\n" + method: importPokemon + specs: + - selector: span[tracetest.span.type="general" name="documentLoad"] + name: Document Load Should be fast + assertions: + - attr:tracetest.span.duration < 500ms + - selector: span[tracetest.span.type="http" http.scheme="http"] + name: All HTTP request should return 200 + assertions: + - attr:http.status_code = 200 + - selector: span[tracetest.span.type="messaging" name="queue.synchronizePokemon process" messaging.system="rabbitmq" messaging.destination="queue.synchronizePokemon" messaging.operation="process"] + name: The worker should be processed + assertions: + - attr:tracetest.selected_spans.count = 1 + - selector: span[tracetest.span.type="database"] + name: "All Database Spans: Processing time is less than 100ms" + assertions: + - attr:tracetest.span.duration < 250ms + +type: Monitor +spec: + id: playwright-monitor + name: Playwright Synthetic Monitor + enabled: true + variableSetId: "" + tokenId: "" + tests: + - import-pokemon + schedule: + cron: "*/5 * * * *" + timeZone: Etc/UTC + alerts: + - id: slack-webhook-message + type: webhook + webhook: + body: "{\n \"text\": \"Monitor ${.Monitor.Name} has failed, follow the link to find the <${.URL}|results>\"\n}" + method: POST + url: + headers: + - key: Content-Type + value: application/json + events: + - FAILED + + ID NAME VERSION RUNS LAST RUN TIME LAST RUN STATE URL +---------------------- --------------------------------- --------- ------ --------------------- ---------------- ------------------------------------------------------- + playwright-monitor Playwright Synthetic Monitor 1 0 https://api.tracetest.io/monitor/playwright-monitor-3 +``` + +## Running Synthetic Monitoring + +1. Select `Monitors` in the [Tracetest](https://app.tracetest.io/) sidebar. +2. You'll see the Monitor that was created in the `apply` script. Click "Run Now". + ![apply script monitor](https://res.cloudinary.com/djwdcmwdz/image/upload/v1722252986/docs/app.tracetest.io_organizations_ttorg_e66318ba6544b856_environments_ttenv_8fca16a31b8b6e24_monitors_oazwzc.png) +3. Select an access token for your Monitor and save the configuration. + ![select admin token](https://res.cloudinary.com/djwdcmwdz/image/upload/v1722253130/docs/app.tracetest.io_organizations_ttorg_e66318ba6544b856_environments_ttenv_8fca16a31b8b6e24_monitors_page_1_8_qg9pgv.png) + +With this setup, your Monitor will trigger the Playwright test every 5 minutes. + +## What's Next? + +After running the test, you can click the run link, update the assertions, and run the scripts once more. This flow enables complete a trace-based TDD flow. + +![assertions](../img/playwright-engine.gif) + +## Learn More + +Please visit our [examples in GitHub](https://github.com/kubeshop/tracetest/tree/main/examples) and join our [Slack Community](https://dub.sh/tracetest-community) for more info! diff --git a/docs/sidebars.js b/docs/sidebars.js index af772b9960..3a7b36c84d 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -141,6 +141,11 @@ const sidebars = { id: "examples-tutorials/recipes/synthetic-monitoring-trace-based-api-tests", label: "Synthetic Monitoring with Trace-based API Tests", }, + { + type: "doc", + id: "examples-tutorials/recipes/synthetic-monitoring-trace-based-playwright-tests", + label: "Synthetic Monitoring with Trace-based Playwright Tests", + }, ], }, { diff --git a/examples/quick-start-nodejs-synthetic-monitoring/readme.md b/examples/quick-start-nodejs-synthetic-monitoring/readme.md index daf711e7db..8e1a790feb 100644 --- a/examples/quick-start-nodejs-synthetic-monitoring/readme.md +++ b/examples/quick-start-nodejs-synthetic-monitoring/readme.md @@ -1,6 +1,6 @@ # Quick Start - Synthetic Monitoring with Trace-based Tests using a Node.js app with OpenTelemetry -> [Read the detailed recipe with Tractest in our documentation.](https://docs.tracetest.io/examples-tutorials/recipes/synthetic-monitoring-trace-based-api-tests) +> [Read the detailed recipe with Tracetest in our documentation.](https://docs.tracetest.io/examples-tutorials/recipes/synthetic-monitoring-trace-based-api-tests) This is a simple quick start on how to configure trace-based synthetic testing / monitoring with a Node.js app and OpenTelemetry instrumentation with traces. This example includes manual instrumentation and a sample bookstore array that simulates fetching data from a database. diff --git a/examples/tracetest-synthetic-monitoring/.github/workflows/synthetic-monitoring.yaml b/examples/tracetest-synthetic-monitoring-github-actions/.github/workflows/synthetic-monitoring.yaml similarity index 100% rename from examples/tracetest-synthetic-monitoring/.github/workflows/synthetic-monitoring.yaml rename to examples/tracetest-synthetic-monitoring-github-actions/.github/workflows/synthetic-monitoring.yaml diff --git a/examples/tracetest-synthetic-monitoring-github-actions/Dockerfile b/examples/tracetest-synthetic-monitoring-github-actions/Dockerfile new file mode 100644 index 0000000000..9ea4b7ab16 --- /dev/null +++ b/examples/tracetest-synthetic-monitoring-github-actions/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:alpine as builder +ENV GO111MODULE=on +RUN apk update && apk add --no-cache git +WORKDIR /app +COPY go.mod ./ +COPY go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./bin/main . +FROM scratch +COPY --from=builder /app/bin/main . +CMD ["./main"] \ No newline at end of file diff --git a/examples/tracetest-synthetic-monitoring-github-actions/docker-compose.yaml b/examples/tracetest-synthetic-monitoring-github-actions/docker-compose.yaml new file mode 100644 index 0000000000..c4a0e00c23 --- /dev/null +++ b/examples/tracetest-synthetic-monitoring-github-actions/docker-compose.yaml @@ -0,0 +1,12 @@ +version: '3' +services: + app: + image: quick-start-go + extra_hosts: + - "host.docker.internal:host-gateway" + build: . + ports: + - "8080:8080" + depends_on: + tracetest: + condition: service_started diff --git a/examples/tracetest-synthetic-monitoring/go.mod b/examples/tracetest-synthetic-monitoring-github-actions/go.mod similarity index 100% rename from examples/tracetest-synthetic-monitoring/go.mod rename to examples/tracetest-synthetic-monitoring-github-actions/go.mod diff --git a/examples/tracetest-synthetic-monitoring/go.sum b/examples/tracetest-synthetic-monitoring-github-actions/go.sum similarity index 100% rename from examples/tracetest-synthetic-monitoring/go.sum rename to examples/tracetest-synthetic-monitoring-github-actions/go.sum diff --git a/examples/tracetest-synthetic-monitoring/main.go b/examples/tracetest-synthetic-monitoring-github-actions/main.go similarity index 100% rename from examples/tracetest-synthetic-monitoring/main.go rename to examples/tracetest-synthetic-monitoring-github-actions/main.go diff --git a/examples/tracetest-synthetic-monitoring-github-actions/readme.md b/examples/tracetest-synthetic-monitoring-github-actions/readme.md new file mode 100644 index 0000000000..313d277ed0 --- /dev/null +++ b/examples/tracetest-synthetic-monitoring-github-actions/readme.md @@ -0,0 +1,12 @@ +# Synthetic monitoring with Tracetest and GitHub Actions + +> [Read the detailed recipe for setting up OpenTelemetry Collector with Tractest in our documentation.](https://docs.tracetest.io/examples-tutorials/recipes/running-tracetest-without-a-trace-data-store) + +This is a simple quick start on how to configure Tracetest and GitHub Actions to emulate synthetic monitoring using your existing Tracetest tests. The idea behind this example +is to have a way of running a set of tests using Tracetest on a schedule and notify a Slack channel in case the test fails. This way, you can keep testing your application and +identifying issues constantly. + +This example is based on the [Golang quick-start example](https://github.com/kubeshop/tracetest/tree/main/examples/quick-start-go). All important information about +how to run synthetic monitoring using Tracetest can be found in the [.github/workflows/synthetic-monitoring.yaml](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-synthetic-monitoring/.github/workflows/synthetic-monitoring.yaml) file. + +Feel free to check out the [docs](https://docs.tracetest.io/), and join our [Slack Community](https://dub.sh/tracetest-community) for more info! diff --git a/examples/tracetest-synthetic-monitoring/test-api.yaml b/examples/tracetest-synthetic-monitoring-github-actions/test-api.yaml similarity index 100% rename from examples/tracetest-synthetic-monitoring/test-api.yaml rename to examples/tracetest-synthetic-monitoring-github-actions/test-api.yaml diff --git a/examples/tracetest-synthetic-monitoring/tracetest/collector.config.yaml b/examples/tracetest-synthetic-monitoring-github-actions/tracetest/collector.config.yaml similarity index 100% rename from examples/tracetest-synthetic-monitoring/tracetest/collector.config.yaml rename to examples/tracetest-synthetic-monitoring-github-actions/tracetest/collector.config.yaml diff --git a/examples/tracetest-synthetic-monitoring/tracetest/docker-compose.yaml b/examples/tracetest-synthetic-monitoring-github-actions/tracetest/docker-compose.yaml similarity index 100% rename from examples/tracetest-synthetic-monitoring/tracetest/docker-compose.yaml rename to examples/tracetest-synthetic-monitoring-github-actions/tracetest/docker-compose.yaml diff --git a/examples/tracetest-synthetic-monitoring/tracetest/tracetest-provision.yaml b/examples/tracetest-synthetic-monitoring-github-actions/tracetest/tracetest-provision.yaml similarity index 100% rename from examples/tracetest-synthetic-monitoring/tracetest/tracetest-provision.yaml rename to examples/tracetest-synthetic-monitoring-github-actions/tracetest/tracetest-provision.yaml diff --git a/examples/tracetest-synthetic-monitoring/tracetest/tracetest.config.yaml b/examples/tracetest-synthetic-monitoring-github-actions/tracetest/tracetest.config.yaml similarity index 100% rename from examples/tracetest-synthetic-monitoring/tracetest/tracetest.config.yaml rename to examples/tracetest-synthetic-monitoring-github-actions/tracetest/tracetest.config.yaml diff --git a/examples/tracetest-synthetic-monitoring/.env.template b/examples/tracetest-synthetic-monitoring/.env.template new file mode 100644 index 0000000000..0f970a2824 --- /dev/null +++ b/examples/tracetest-synthetic-monitoring/.env.template @@ -0,0 +1,2 @@ +TRACETEST_API_TOKEN= +TRACETEST_ENVIRONMENT_ID= diff --git a/examples/tracetest-synthetic-monitoring/.gitignore b/examples/tracetest-synthetic-monitoring/.gitignore new file mode 100644 index 0000000000..c4e8a2f309 --- /dev/null +++ b/examples/tracetest-synthetic-monitoring/.gitignore @@ -0,0 +1,13 @@ +node_modules +.vscode + +node_modules/ +api/.build/ +.idea/ +.DS_Store +.env + +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/examples/tracetest-synthetic-monitoring/Dockerfile b/examples/tracetest-synthetic-monitoring/Dockerfile index 9ea4b7ab16..883f239155 100644 --- a/examples/tracetest-synthetic-monitoring/Dockerfile +++ b/examples/tracetest-synthetic-monitoring/Dockerfile @@ -1,12 +1,11 @@ -FROM golang:alpine as builder -ENV GO111MODULE=on -RUN apk update && apk add --no-cache git +FROM alpine + WORKDIR /app -COPY go.mod ./ -COPY go.sum ./ -RUN go mod download -COPY . . -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./bin/main . -FROM scratch -COPY --from=builder /app/bin/main . -CMD ["./main"] \ No newline at end of file +ARG TRACETEST_IMAGE_VERSION=v1.4.0 + +RUN apk --update add bash jq curl +RUN curl -L https://raw.githubusercontent.com/kubeshop/tracetest/main/install-cli.sh | bash -s -- $TRACETEST_IMAGE_VERSION + +WORKDIR /resources + +ENTRYPOINT ["echo", "Tracetest CLI installed"] diff --git a/examples/tracetest-synthetic-monitoring/collector.config.yaml b/examples/tracetest-synthetic-monitoring/collector.config.yaml new file mode 100644 index 0000000000..610b586a26 --- /dev/null +++ b/examples/tracetest-synthetic-monitoring/collector.config.yaml @@ -0,0 +1,27 @@ +receivers: + otlp: + protocols: + grpc: + http: + cors: + allowed_origins: + - "http://*" + - "https://*" + +processors: + batch: + +exporters: + logging: + loglevel: debug + jaeger: + endpoint: jaeger:14250 + tls: + insecure: true + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [logging, jaeger] diff --git a/examples/tracetest-synthetic-monitoring/docker-compose.yaml b/examples/tracetest-synthetic-monitoring/docker-compose.yaml index c4a0e00c23..93e4e68015 100644 --- a/examples/tracetest-synthetic-monitoring/docker-compose.yaml +++ b/examples/tracetest-synthetic-monitoring/docker-compose.yaml @@ -1,12 +1,149 @@ -version: '3' +version: "3.5" +name: tracetest-synthetic-monitoring-playwright-engine + services: - app: - image: quick-start-go + tracetest-apply: + build: . + volumes: + - ./resources:/resources + environment: + TRACETEST_API_KEY: ${TRACETEST_API_TOKEN} + TRACETEST_ENVIRONMENT_ID: ${TRACETEST_ENVIRONMENT_ID} + entrypoint: + - bash + - /resources/apply.sh + networks: + default: null + depends_on: + api: + condition: service_healthy + tracetest-agent: + condition: service_started + + tracetest-run: + build: . + volumes: + - ./resources:/resources + environment: + TRACETEST_API_KEY: ${TRACETEST_API_TOKEN} + TRACETEST_ENVIRONMENT_ID: ${TRACETEST_ENVIRONMENT_ID} + entrypoint: + - bash + - /resources/run.sh + networks: + default: null + depends_on: + tracetest-apply: + condition: service_completed_successfully + + tracetest-agent: + environment: + TRACETEST_API_KEY: ${TRACETEST_API_TOKEN} + image: kubeshop/tracetest-agent:latest + networks: + default: null + + # pokeshop demo services + postgres: + image: postgres:14 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 1s + timeout: 5s + retries: 60 + + cache: + image: redis:6 + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 1s + timeout: 3s + retries: 60 + + queue: + image: rabbitmq:3.12 + restart: unless-stopped + healthcheck: + test: rabbitmq-diagnostics -q check_running + interval: 1s + timeout: 5s + retries: 60 + + otel-collector: + image: otel/opentelemetry-collector-contrib:0.59.0 + restart: unless-stopped extra_hosts: - "host.docker.internal:host-gateway" - build: . + command: + - "--config" + - "/otel-local-config.yaml" + volumes: + - ./collector.config.yaml:/otel-local-config.yaml + + api: + image: kubeshop/demo-pokemon-api:latest + restart: unless-stopped + pull_policy: always + environment: + REDIS_URL: cache + DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres?schema=public + RABBITMQ_HOST: queue + POKE_API_BASE_URL: https://pokeapi.co/api/v2 + COLLECTOR_ENDPOINT: http://otel-collector:4317 + HTTP_COLLECTOR_ENDPOINT: http://otel-collector:4318/v1/traces + NPM_RUN_COMMAND: api + healthcheck: + test: ["CMD", "wget", "--spider", "localhost:8081"] + interval: 1s + timeout: 3s + retries: 60 ports: - - "8080:8080" + - 8081:8081 depends_on: - tracetest: + postgres: + condition: service_healthy + cache: + condition: service_healthy + queue: + condition: service_healthy + jaeger: + condition: service_started + otel-collector: + condition: service_started + worker: condition: service_started + + worker: + image: kubeshop/demo-pokemon-api:latest + restart: unless-stopped + pull_policy: always + environment: + REDIS_URL: cache + DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres?schema=public + RABBITMQ_HOST: queue + POKE_API_BASE_URL: https://pokeapi.co/api/v2 + COLLECTOR_ENDPOINT: http://otel-collector:4317 + NPM_RUN_COMMAND: worker + depends_on: + postgres: + condition: service_healthy + cache: + condition: service_healthy + queue: + condition: service_healthy + + jaeger: + image: jaegertracing/all-in-one:latest + ports: + - 14250:14250 + - 16685:16685 + - 16686:16686 + healthcheck: + test: ["CMD", "wget", "--spider", "localhost:16686"] + interval: 1s + timeout: 3s + retries: 60 diff --git a/examples/tracetest-synthetic-monitoring/readme.md b/examples/tracetest-synthetic-monitoring/readme.md index 313d277ed0..4260386266 100644 --- a/examples/tracetest-synthetic-monitoring/readme.md +++ b/examples/tracetest-synthetic-monitoring/readme.md @@ -1,12 +1,15 @@ -# Synthetic monitoring with Tracetest and GitHub Actions +# Tracetest Monitors - Synthetic Monitoring -> [Read the detailed recipe for setting up OpenTelemetry Collector with Tractest in our documentation.](https://docs.tracetest.io/examples-tutorials/recipes/running-tracetest-without-a-trace-data-store) +This repository's objective is to show how you can configure trace-based tests to run as synthetic monitors using Tracetest Monitors. -This is a simple quick start on how to configure Tracetest and GitHub Actions to emulate synthetic monitoring using your existing Tracetest tests. The idea behind this example -is to have a way of running a set of tests using Tracetest on a schedule and notify a Slack channel in case the test fails. This way, you can keep testing your application and -identifying issues constantly. +## Documentation Recipe -This example is based on the [Golang quick-start example](https://github.com/kubeshop/tracetest/tree/main/examples/quick-start-go). All important information about -how to run synthetic monitoring using Tracetest can be found in the [.github/workflows/synthetic-monitoring.yaml](https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-synthetic-monitoring/.github/workflows/synthetic-monitoring.yaml) file. +This example is part of the official Tracetest docs and can be found by following this [link](https://docs.tracetest.io/examples-tutorials/recipes/synthetic-monitoring-trace-based-playwright-tests). -Feel free to check out the [docs](https://docs.tracetest.io/), and join our [Slack Community](https://dub.sh/tracetest-community) for more info! +## Steps + +1. Copy the `.env.template` file to `.env`. +2. Log into the [Tracetest app](https://app.tracetest.io/). +3. Fill out the [TRACETEST_API_TOKEN](https://docs.tracetest.io/concepts/environment-tokens) with an admin role token and the [TRACETEST_ENVIRONMENT_ID](https://docs.tracetest.io/concepts/environments) with the id of your environment. +4. Run `docker compose run tracetest-apply`. +5. Follow the links in the log to view the created synthetic monitors. diff --git a/examples/tracetest-synthetic-monitoring/resources/apply.sh b/examples/tracetest-synthetic-monitoring/resources/apply.sh new file mode 100644 index 0000000000..fb32cb77a9 --- /dev/null +++ b/examples/tracetest-synthetic-monitoring/resources/apply.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +TOKEN=$TRACETEST_API_KEY +ENVIRONMENT_ID=$TRACETEST_ENVIRONMENT_ID + +apply() { + echo "Configuring Tracetest" + tracetest configure --token $TOKEN --environment $ENVIRONMENT_ID + + echo "Applying Resources" + tracetest apply datastore -f /resources/datastore.yaml + tracetest apply test -f /resources/import-pokemon.yaml + tracetest apply monitor -f /resources/monitor.yaml + tracetest list monitor +} + +apply diff --git a/examples/tracetest-synthetic-monitoring/resources/datastore.yaml b/examples/tracetest-synthetic-monitoring/resources/datastore.yaml new file mode 100644 index 0000000000..72f8294fcd --- /dev/null +++ b/examples/tracetest-synthetic-monitoring/resources/datastore.yaml @@ -0,0 +1,12 @@ +type: DataStore +spec: + id: current + name: jaeger + type: jaeger + default: true + jaeger: + endpoint: jaeger:16685 + headers: + "": "" + tls: + insecure: true diff --git a/examples/tracetest-synthetic-monitoring/resources/import-pokemon.yaml b/examples/tracetest-synthetic-monitoring/resources/import-pokemon.yaml new file mode 100644 index 0000000000..6eebcf5e1d --- /dev/null +++ b/examples/tracetest-synthetic-monitoring/resources/import-pokemon.yaml @@ -0,0 +1,31 @@ +type: Test +spec: + id: import-pokemon + name: Import Pokemon + trigger: + type: playwrightengine + playwrightEngine: + target: http://api:8081 + script: ./script.js + method: importPokemon + specs: + - selector: span[tracetest.span.type="general" name="documentLoad"] + name: Document Load Should be fast + assertions: + - attr:tracetest.span.duration < 500ms + - selector: span[tracetest.span.type="http" http.scheme="http"] + name: All HTTP request should return 200 + assertions: + - attr:http.status_code = 200 + - selector: + span[tracetest.span.type="messaging" name="queue.synchronizePokemon + process" messaging.system="rabbitmq" + messaging.destination="queue.synchronizePokemon" + messaging.operation="process"] + name: The worker should be processed + assertions: + - attr:tracetest.selected_spans.count = 1 + - selector: span[tracetest.span.type="database"] + name: "All Database Spans: Processing time is less than 100ms" + assertions: + - attr:tracetest.span.duration < 250ms diff --git a/examples/tracetest-synthetic-monitoring/resources/monitor.yaml b/examples/tracetest-synthetic-monitoring/resources/monitor.yaml new file mode 100644 index 0000000000..a00bbbb5d4 --- /dev/null +++ b/examples/tracetest-synthetic-monitoring/resources/monitor.yaml @@ -0,0 +1,24 @@ +type: Monitor +spec: + id: playwright-monitor + name: Playwright Synthetic Monitor + enabled: true + # variableSetId: "" + # tokenId: ${TOKEN} # Replace with your token id + tests: + - import-pokemon + schedule: + cron: "*/5 * * * *" + timeZone: Etc/UTC + alerts: + - id: slack-webhook-message + type: webhook + webhook: + body: "{\n \"text\": \"Monitor ${.Monitor.Name} has failed, follow the link to find the <${.URL}|results>\"\n}" + method: POST + url: + headers: + - key: Content-Type + value: application/json + events: + - FAILED diff --git a/examples/tracetest-synthetic-monitoring/resources/script.js b/examples/tracetest-synthetic-monitoring/resources/script.js new file mode 100644 index 0000000000..d77990406e --- /dev/null +++ b/examples/tracetest-synthetic-monitoring/resources/script.js @@ -0,0 +1,36 @@ +const { expect } = require("@playwright/test"); + +async function addPokemon(page) { + expect(await page.getByText("Pokeshop")).toBeTruthy(); + + await page.click("text=Add"); + + await page.getByLabel("Name").fill("Charizard"); + await page.getByLabel("Type").fill("Flying"); + await page + .getByLabel("Image URL") + .fill("https://upload.wikimedia.org/wikipedia/en/1/1f/Pok%C3%A9mon_Charizard_art.png"); + await page.getByRole("button", { name: "OK", exact: true }).click(); +} + +async function deletePokemon(page) { + expect(await page.getByText("Pokeshop")).toBeTruthy(); + + await page.locator('[data-cy="pokemon-list"]'); + await page.locator('[data-cy="pokemon-card"]').first().click(); + await page.locator('[data-cy="pokemon-card"] [data-cy="delete-pokemon-button"]').first().click(); +} + +async function importPokemon(page) { + expect(await page.getByText("Pokeshop")).toBeTruthy(); + + await page.click("text=Import"); + await page.getByLabel("ID").fill("143"); + + await Promise.all([ + page.waitForResponse((resp) => resp.url().includes("/pokemon/import") && resp.status() === 200), + page.getByRole("button", { name: "OK", exact: true }).click(), + ]); +} + +module.exports = { addPokemon, deletePokemon, importPokemon };