diff --git a/internal/coreinternal/scraperinttest/scraperint.go b/internal/coreinternal/scraperinttest/scraperint.go index 55421515016c..18a95b13f697 100644 --- a/internal/coreinternal/scraperinttest/scraperint.go +++ b/internal/coreinternal/scraperinttest/scraperint.go @@ -58,7 +58,8 @@ type IntegrationTest struct { compareOptions []pmetrictest.CompareMetricsOption compareTimeout time.Duration - writeExpected bool + failOnErrorLogs bool + writeExpected bool } func (it *IntegrationTest) Run(t *testing.T) { @@ -122,6 +123,14 @@ func (it *IntegrationTest) Run(t *testing.T) { if len(allMetrics) == 0 { return false } + if it.failOnErrorLogs && len(observedLogs.All()) > 0 { + logs := strings.Builder{} + for _, e := range observedLogs.All() { + logs.WriteString(e.Message + "\n") + } + t.Errorf("full log:\n%s", logs.String()) + } + if it.writeExpected { require.NoError(t, golden.WriteMetrics(t, it.expectedFile, allMetrics[0])) return true @@ -229,6 +238,15 @@ func WithExpectedFile(f string) TestOption { } } +// This option is useful for debugging scrapers but should not be used permanently +// because the logs do not correlate to a single scrape interval. In other words, +// when a retryable failure occurs, this setting will likely force a failure anyways. +func FailOnErrorLogs() TestOption { + return func(it *IntegrationTest) { + it.failOnErrorLogs = true + } +} + func WriteExpected() TestOption { return func(it *IntegrationTest) { it.writeExpected = true diff --git a/receiver/mysqlreceiver/README.md b/receiver/mysqlreceiver/README.md index f2a9c5b0d133..88f47d823ee9 100644 --- a/receiver/mysqlreceiver/README.md +++ b/receiver/mysqlreceiver/README.md @@ -18,7 +18,7 @@ This receiver queries MySQL's global status and InnoDB tables. This receiver supports MySQL version 8.0 -Collecting most metrics requires the ability to execute `SHOW GLOBAL STATUS`. The `buffer_pool_size` metric requires access to the `information_schema.innodb_metrics` table. Please refer to [setup.sh](./testdata/integration/scripts/setup.sh) for an example of how to configure these permissions. +Collecting most metrics requires the ability to execute `SHOW GLOBAL STATUS`. ## Configuration diff --git a/receiver/mysqlreceiver/integration_test.go b/receiver/mysqlreceiver/integration_test.go index 962f82c74064..dc58a2e31fd5 100644 --- a/receiver/mysqlreceiver/integration_test.go +++ b/receiver/mysqlreceiver/integration_test.go @@ -27,18 +27,23 @@ func TestIntegration(t *testing.T) { NewFactory(), scraperinttest.WithContainerRequest( testcontainers.ContainerRequest{ - FromDockerfile: testcontainers.FromDockerfile{ - Context: filepath.Join("testdata", "integration"), - Dockerfile: "Dockerfile.mysql", - }, + Image: "mysql:8.0.33", ExposedPorts: []string{mysqlPort}, WaitingFor: wait.ForListeningPort(mysqlPort). WithStartupTimeout(2 * time.Minute), - LifecycleHooks: []testcontainers.ContainerLifecycleHooks{{ - PostStarts: []testcontainers.ContainerHook{ - scraperinttest.RunScript([]string{"/setup.sh"}), + Env: map[string]string{ + "MYSQL_ROOT_PASSWORD": "otel", + "MYSQL_DATABASE": "otel", + "MYSQL_USER": "otel", + "MYSQL_PASSWORD": "otel", + }, + Files: []testcontainers.ContainerFile{ + { + HostFilePath: filepath.Join("testdata", "integration", "init.sh"), + ContainerFilePath: "/docker-entrypoint-initdb.d/init.sh", + FileMode: 700, }, - }}, + }, }), scraperinttest.WithCustomConfig( func(t *testing.T, cfg component.Config, ci *scraperinttest.ContainerInfo) { diff --git a/receiver/mysqlreceiver/scraper.go b/receiver/mysqlreceiver/scraper.go index 0dadd833d47f..441e438552e8 100644 --- a/receiver/mysqlreceiver/scraper.go +++ b/receiver/mysqlreceiver/scraper.go @@ -102,7 +102,7 @@ func (m *mySQLScraper) scrape(context.Context) (pmetric.Metrics, error) { m.scrapeGlobalStats(now, errs) // colect replicas status metrics. - m.scrapeReplicaStatusStats(now, errs) + m.scrapeReplicaStatusStats(now) m.mb.EmitForResource(metadata.WithMysqlInstanceEndpoint(m.config.Endpoint)) @@ -528,11 +528,10 @@ func (m *mySQLScraper) scrapeTableLockWaitEventStats(now pcommon.Timestamp, errs } } -func (m *mySQLScraper) scrapeReplicaStatusStats(now pcommon.Timestamp, errs *scrapererror.ScrapeErrors) { +func (m *mySQLScraper) scrapeReplicaStatusStats(now pcommon.Timestamp) { replicaStatusStats, err := m.sqlclient.getReplicaStatusStats() if err != nil { - m.logger.Error("Failed to fetch replica status stats", zap.Error(err)) - errs.AddPartial(8, err) + m.logger.Info("Failed to fetch replica status stats", zap.Error(err)) return } diff --git a/receiver/mysqlreceiver/testdata/integration/Dockerfile.mysql b/receiver/mysqlreceiver/testdata/integration/Dockerfile.mysql deleted file mode 100644 index 6f60d14bc9cb..000000000000 --- a/receiver/mysqlreceiver/testdata/integration/Dockerfile.mysql +++ /dev/null @@ -1,9 +0,0 @@ -FROM mysql:8.0.33 - -COPY scripts/setup.sh /setup.sh -RUN chmod +x /setup.sh - -ENV MYSQL_DATABASE=otel -ENV MYSQL_USER=otel -ENV MYSQL_PASSWORD=otel -ENV MYSQL_ROOT_PASSWORD=otel diff --git a/receiver/mysqlreceiver/testdata/integration/init.sh b/receiver/mysqlreceiver/testdata/integration/init.sh new file mode 100644 index 000000000000..b5151eb6223d --- /dev/null +++ b/receiver/mysqlreceiver/testdata/integration/init.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +set -e + +USER="otel" +ROOT_PASS="otel" + +# # NOTE: -pPASSWORD is missing a space on purpose +mysql -u root -p"${ROOT_PASS}" -e "GRANT PROCESS ON *.* TO ${USER}" > /dev/null +mysql -u root -p"${ROOT_PASS}" -e "GRANT SELECT ON performance_schema.* TO ${USER}" > /dev/null +mysql -u root -p"${ROOT_PASS}" -e "FLUSH PRIVILEGES" > /dev/null diff --git a/receiver/mysqlreceiver/testdata/integration/scripts/setup.sh b/receiver/mysqlreceiver/testdata/integration/scripts/setup.sh deleted file mode 100644 index f67f0c995dd9..000000000000 --- a/receiver/mysqlreceiver/testdata/integration/scripts/setup.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The OpenTelemetry Authors -# SPDX-License-Identifier: Apache-2.0 - -set -e - -USER="otel" -ROOT_PASS="otel" -CODE=1 - - -setup_permissions() { - # NOTE: -pPASSWORD is missing a space on purpose - mysql -u root -p"${ROOT_PASS}" -e "GRANT PROCESS ON *.* TO ${USER}" > /dev/null - mysql -u root -p"${ROOT_PASS}" -e "GRANT SELECT ON INFORMATION_SCHEMA.INNODB_METRICS TO ${USER}" > /dev/null - mysql -u root -p"${ROOT_PASS}" -e "FLUSH PRIVILEGES" > /dev/null -} - -setup_data() { - mysql -u root -p"${ROOT_PASS}" -e "CREATE DATABASE a_schema" > /dev/null - mysql -u root -p"${ROOT_PASS}" -e "CREATE TABLE a_schema.a_table (k int, v int)" > /dev/null - mysql -u root -p"${ROOT_PASS}" -e "CREATE INDEX an_index ON a_schema.a_table ((k + v))" > /dev/null -} - -echo "Configuring ${USER} permissions. . ." -end=$((SECONDS+60)) -while [ $SECONDS -lt $end ]; do - result="$?" - if setup_permissions; then - echo "Permissions configured!" - while [ $SECONDS -lt $end ]; do - result="$?" - if setup_data; then - echo "Data created!" - exit 0 - fi - echo "Trying again in 5 seconds. . ." - sleep 5 - done - fi - echo "Trying again in 5 seconds. . ." - sleep 5 -done - -echo "Failed to configure permissions"