diff --git a/integration-tests/docker/docker-compose.yaml b/integration-tests/docker/docker-compose.yaml index b773503732f..e509390b794 100644 --- a/integration-tests/docker/docker-compose.yaml +++ b/integration-tests/docker/docker-compose.yaml @@ -131,6 +131,43 @@ services: networks: - integration-tests + mysql: + image: mysql:8.0 + container_name: mysql + ports: + - "3306:3306" + environment: + MYSQL_ROOT_PASSWORD: rootpassword + MYSQL_DATABASE: testdb + volumes: + - ./tests/database-observability-mysql/init_mysql.sh:/docker-entrypoint-initdb.d/init_mysql.sh + networks: + - integration-tests + + postgres: + image: postgres:16 + container_name: postgres + ports: + - "5432:5432" + environment: + POSTGRES_USER: root + POSTGRES_PASSWORD: rootpassword + POSTGRES_DB: testdb + command: ["postgres", "-c", "shared_preload_libraries=pg_stat_statements", "-c", "pg_stat_statements.track=all"] + networks: + - integration-tests + + postgres-init: + image: postgres:16 + depends_on: + - postgres + volumes: + - ./tests/database-observability-postgres/init_postgres.sh:/init_postgres.sh + command: sh /init_postgres.sh + restart: "no" + networks: + - integration-tests + networks: integration-tests: driver: bridge diff --git a/integration-tests/docker/tests/database-observability-mysql/config.alloy b/integration-tests/docker/tests/database-observability-mysql/config.alloy new file mode 100644 index 00000000000..b04d5e4a13f --- /dev/null +++ b/integration-tests/docker/tests/database-observability-mysql/config.alloy @@ -0,0 +1,55 @@ +// WARNING: This configuration is for integration testing only. +// It is not intended to represent a recommended production configuration. + +prometheus.exporter.mysql "mysql" { + data_source_name = "root:rootpassword@tcp(mysql:3306)/testdb" +} + +database_observability.mysql "test" { + data_source_name = "root:rootpassword@tcp(mysql:3306)/testdb" + forward_to = [loki.write.default.receiver] + targets = prometheus.exporter.mysql.mysql.targets + + query_samples { + collect_interval = "1s" + } + + query_details { + collect_interval = "1s" + } + + schema_details { + collect_interval = "1s" + } + + explain_plans { + collect_interval = "1s" + } + + health_check { + collect_interval = "1s" + } +} + +prometheus.scrape "mysql" { + targets = database_observability.mysql.test.targets + forward_to = [prometheus.remote_write.default.receiver] +} + +prometheus.remote_write "default" { + endpoint { + url = "http://mimir:9009/api/v1/push" + } + external_labels = { + test_name = "database_observability_mysql", + } +} + +loki.write "default" { + endpoint { + url = "http://loki:3100/loki/api/v1/push" + } + external_labels = { + test_name = "database_observability_mysql", + } +} diff --git a/integration-tests/docker/tests/database-observability-mysql/init_mysql.sh b/integration-tests/docker/tests/database-observability-mysql/init_mysql.sh new file mode 100755 index 00000000000..b52518f6328 --- /dev/null +++ b/integration-tests/docker/tests/database-observability-mysql/init_mysql.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -e + +echo "Creating test tables..." + +mysql -u root -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" < 20; +SELECT * FROM orders WHERE status = 'pending'; +SELECT p.name, o.total FROM products p JOIN orders o ON p.id = o.product_id; +EOF + +echo "Test tables and data created successfully!" diff --git a/integration-tests/docker/tests/database-observability-mysql/mysql_test.go b/integration-tests/docker/tests/database-observability-mysql/mysql_test.go new file mode 100644 index 00000000000..17e133e098d --- /dev/null +++ b/integration-tests/docker/tests/database-observability-mysql/mysql_test.go @@ -0,0 +1,52 @@ +//go:build alloyintegrationtests + +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/grafana/alloy/integration-tests/docker/common" +) + +const testName = "database_observability_mysql" + +func TestDatabaseObservabilityMySQLMetrics(t *testing.T) { + var metrics = []string{ + "database_observability_connection_info", + } + common.MimirMetricsTest(t, metrics, []string{}, testName) +} + +func TestDatabaseObservabilityMySQLLogs(t *testing.T) { + common.AssertStatefulTestEnv(t) + + expectedOps := []string{ + "health_status", + "query_association", + "query_parsed_table_name", + "schema_detection", + "table_detection", + "create_statement", + } + + var logResponse common.LogResponse + + require.EventuallyWithT(t, func(c *assert.CollectT) { + _, err := common.FetchDataFromURL(common.LogQuery(testName, 100), &logResponse) + assert.NoError(c, err) + + ops := make(map[string]bool) + for _, result := range logResponse.Data.Result { + if op, ok := result.Stream["op"]; ok { + ops[op] = true + } + } + + for _, op := range expectedOps { + assert.True(c, ops[op], "expected %s logs", op) + } + }, common.TestTimeoutEnv(t), common.DefaultRetryInterval) +} diff --git a/integration-tests/docker/tests/database-observability-postgres/config.alloy b/integration-tests/docker/tests/database-observability-postgres/config.alloy new file mode 100644 index 00000000000..c9d57213465 --- /dev/null +++ b/integration-tests/docker/tests/database-observability-postgres/config.alloy @@ -0,0 +1,55 @@ +// WARNING: This configuration is for integration testing only. +// It is not intended to represent a recommended production configuration. + +prometheus.exporter.postgres "postgres" { + data_source_names = ["postgresql://root:rootpassword@postgres:5432/testdb?sslmode=disable"] +} + +database_observability.postgres "test" { + data_source_name = "postgresql://root:rootpassword@postgres:5432/testdb?sslmode=disable" + forward_to = [loki.write.default.receiver] + targets = prometheus.exporter.postgres.postgres.targets + + query_samples { + collect_interval = "1s" + } + + query_details { + collect_interval = "1s" + } + + schema_details { + collect_interval = "1s" + } + + explain_plans { + collect_interval = "1s" + } + + health_check { + collect_interval = "1s" + } +} + +prometheus.scrape "postgres" { + targets = database_observability.postgres.test.targets + forward_to = [prometheus.remote_write.default.receiver] +} + +prometheus.remote_write "default" { + endpoint { + url = "http://mimir:9009/api/v1/push" + } + external_labels = { + test_name = "database_observability_postgres", + } +} + +loki.write "default" { + endpoint { + url = "http://loki:3100/loki/api/v1/push" + } + external_labels = { + test_name = "database_observability_postgres", + } +} diff --git a/integration-tests/docker/tests/database-observability-postgres/init_postgres.sh b/integration-tests/docker/tests/database-observability-postgres/init_postgres.sh new file mode 100644 index 00000000000..0e0a2930ad5 --- /dev/null +++ b/integration-tests/docker/tests/database-observability-postgres/init_postgres.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -e + +# Wait for PostgreSQL to be ready +echo "Waiting for PostgreSQL to be ready..." +until PGPASSWORD=rootpassword psql -h postgres -U root -d testdb -c "SELECT 1" > /dev/null 2>&1; do + sleep 1 +done + +echo "PostgreSQL is ready. Enabling pg_stat_statements and creating test tables..." + +PGPASSWORD=rootpassword psql -h postgres -U root -d testdb < 20; +SELECT * FROM orders WHERE status = 'pending'; +SELECT p.name, o.total FROM products p JOIN orders o ON p.id = o.product_id; +EOF + +echo "Test tables and data created successfully!" diff --git a/integration-tests/docker/tests/database-observability-postgres/postgres_test.go b/integration-tests/docker/tests/database-observability-postgres/postgres_test.go new file mode 100644 index 00000000000..8142129e6b4 --- /dev/null +++ b/integration-tests/docker/tests/database-observability-postgres/postgres_test.go @@ -0,0 +1,51 @@ +//go:build alloyintegrationtests + +package main + +import ( + "testing" + + "github.com/grafana/alloy/integration-tests/docker/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const testName = "database_observability_postgres" + +func TestDatabaseObservabilityPostgresMetrics(t *testing.T) { + var metrics = []string{ + "database_observability_connection_info", + } + common.MimirMetricsTest(t, metrics, []string{}, testName) +} + +func TestDatabaseObservabilityPostgresLogs(t *testing.T) { + common.AssertStatefulTestEnv(t) + + expectedOps := []string{ + "health_status", + "query_association", + "query_parsed_table_name", + "schema_detection", + "table_detection", + "create_statement", + } + + var logResponse common.LogResponse + + require.EventuallyWithT(t, func(c *assert.CollectT) { + _, err := common.FetchDataFromURL(common.LogQuery(testName, 100), &logResponse) + assert.NoError(c, err) + + ops := make(map[string]bool) + for _, result := range logResponse.Data.Result { + if op, ok := result.Stream["op"]; ok { + ops[op] = true + } + } + + for _, op := range expectedOps { + assert.True(c, ops[op], "expected %s logs", op) + } + }, common.TestTimeoutEnv(t), common.DefaultRetryInterval) +}