diff --git a/collector/exporter.go b/collector/exporter.go index a8ac64ff..ec6977f4 100644 --- a/collector/exporter.go +++ b/collector/exporter.go @@ -132,7 +132,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) { func (e *Exporter) scrape(ctx context.Context, ch chan<- prometheus.Metric) float64 { var err error scrapeTime := time.Now() - instance, err := newInstance(e.dsn) + instance, err := newInstance(ctx, e.dsn) if err != nil { e.logger.Error("Error opening connection to database", "err", err) return 0.0 diff --git a/collector/exporter_test.go b/collector/exporter_test.go index 8869eaed..5cd51e05 100644 --- a/collector/exporter_test.go +++ b/collector/exporter_test.go @@ -14,7 +14,6 @@ package collector import ( - "context" "testing" "github.com/prometheus/client_golang/prometheus" @@ -31,7 +30,7 @@ func TestExporter(t *testing.T) { } exporter := New( - context.Background(), + t.Context(), dsn, []Scraper{ ScrapeGlobalStatus{}, @@ -70,7 +69,7 @@ func TestExporterDSN(t *testing.T) { convey.Convey("DSN with special characters in password (w/o table)", t, func() { dsn := "test:UfY9s73Gx`~!?@#$%^&*(){}[]<>|/:;,.-_+=@tcp(localhost:3306)/" exporter := New( - context.Background(), + t.Context(), dsn, []Scraper{ ScrapeGlobalStatus{}, @@ -83,7 +82,7 @@ func TestExporterDSN(t *testing.T) { convey.Convey("DSN with special characters in password (with table)", t, func() { dsn := "test:UfY9s73Gx`~!?@#$%^&*(){}[]<>|/:;,.-_+=@tcp(localhost:3306)/mysql" exporter := New( - context.Background(), + t.Context(), dsn, []Scraper{ ScrapeGlobalStatus{}, @@ -96,7 +95,7 @@ func TestExporterDSN(t *testing.T) { convey.Convey("DSN with special characters in password, with tls", t, func() { dsn := "test:UfY9s73Gx`~!?@#$%^&*(){}[]<>|/:;,.-_+=@tcp(localhost:3306)/?tls=true" exporter := New( - context.Background(), + t.Context(), dsn, []Scraper{ ScrapeGlobalStatus{}, @@ -109,7 +108,7 @@ func TestExporterDSN(t *testing.T) { convey.Convey("DSN with special characters in password, no tls", t, func() { dsn := "test:UfY9s73Gx`~!?@#$%^&*(){}[]<>|/:;,.-_+=@tcp(localhost:3306)/test?tls=skip-verify" exporter := New( - context.Background(), + t.Context(), dsn, []Scraper{ ScrapeGlobalStatus{}, @@ -126,7 +125,7 @@ func TestGetMySQLVersion(t *testing.T) { } convey.Convey("Version parsing", t, func() { - instance, err := newInstance(dsn) + instance, err := newInstance(t.Context(), dsn) convey.So(err, convey.ShouldBeNil) convey.So(instance.versionMajorMinor, convey.ShouldBeBetweenOrEqual, 5.7, 11.4) diff --git a/collector/instance.go b/collector/instance.go index 60db943f..188e8376 100644 --- a/collector/instance.go +++ b/collector/instance.go @@ -14,6 +14,7 @@ package collector import ( + "context" "database/sql" "fmt" "regexp" @@ -25,9 +26,10 @@ import ( ) const ( - FlavorMySQL = "mysql" - FlavorMariaDB = "mariadb" - versionQuery = "SELECT @@version;" + FlavorMySQL = "mysql" + FlavorMariaDB = "mariadb" + versionQuery = "SELECT @@version;" + performanceSchemaQuery = "SELECT @@performance_schema;" ) var ( @@ -46,38 +48,24 @@ var ( ) type instance struct { - db *sql.DB - flavor string - version semver.Version - versionMajorMinor float64 + db *sql.DB + flavor string + version semver.Version + versionMajorMinor float64 + isPerformanceSchemaEnabled bool } -func newInstance(dsn string) (*instance, error) { - i := &instance{} - db, err := sql.Open("mysql", dsn) +func (i *instance) loadMetadata(ctx context.Context) error { + version, versionString, err := queryVersion(ctx, i.db) if err != nil { - return nil, err - } - db.SetMaxOpenConns(*exporterMaxOpenConns) - db.SetMaxIdleConns(*exporterMaxIdleConns) - db.SetConnMaxLifetime(*exporterConnMaxLifetime) - - i.db = db - - version, versionString, err := queryVersion(db) - if err != nil { - db.Close() - return nil, err + return err } - i.version = version versionMajorMinor, err := strconv.ParseFloat(fmt.Sprintf("%d.%d", i.version.Major, i.version.Minor), 64) if err != nil { - db.Close() - return nil, err + return err } - i.versionMajorMinor = versionMajorMinor if strings.Contains(strings.ToLower(versionString), "mariadb") { @@ -86,9 +74,48 @@ func newInstance(dsn string) (*instance, error) { i.flavor = FlavorMySQL } + isPerformanceSchemaEnabled, err := queryPerformanceSchemaEnabled(ctx, i.db) + if err != nil { + return err + } + i.isPerformanceSchemaEnabled = isPerformanceSchemaEnabled + + return nil +} + +func newInstance(ctx context.Context, dsn string) (*instance, error) { + db, err := sql.Open("mysql", dsn) + if err != nil { + return nil, err + } + + return newInstanceFromDB(ctx, db) +} + +func newInstanceFromDB(ctx context.Context, db *sql.DB) (*instance, error) { + i := &instance{db: db} + i.db.SetMaxOpenConns(*exporterMaxOpenConns) + i.db.SetMaxIdleConns(*exporterMaxIdleConns) + i.db.SetConnMaxLifetime(*exporterConnMaxLifetime) + + if err := i.loadMetadata(ctx); err != nil { + i.db.Close() + return nil, err + } + return i, nil } +// queryPerformanceSchemaEnabled reports whether performance_schema is enabled in the server +func queryPerformanceSchemaEnabled(ctx context.Context, db *sql.DB) (bool, error) { + var enabled uint8 + err := db.QueryRowContext(ctx, performanceSchemaQuery).Scan(&enabled) + if err != nil { + return false, fmt.Errorf("failed to query performance_schema status: %w", err) + } + return enabled == 1, nil +} + // getDB returns the database connection for the instance. func (i *instance) getDB() *sql.DB { return i.db @@ -115,9 +142,9 @@ func (i *instance) Ping() error { // for MySQL: "8.0.36-28.1" var versionRegex = regexp.MustCompile(`^((\d+)(\.\d+)(\.\d+))`) -func queryVersion(db *sql.DB) (semver.Version, string, error) { +func queryVersion(ctx context.Context, db *sql.DB) (semver.Version, string, error) { var version string - err := db.QueryRow(versionQuery).Scan(&version) + err := db.QueryRowContext(ctx, versionQuery).Scan(&version) if err != nil { return semver.Version{}, version, err } diff --git a/collector/instance_test.go b/collector/instance_test.go index dc90d5dc..ba11bd95 100644 --- a/collector/instance_test.go +++ b/collector/instance_test.go @@ -33,32 +33,32 @@ func TestGetMySQLVersion_Percona(t *testing.T) { var semVer semver.Version var err error mock.ExpectQuery(versionQuery).WillReturnRows(sqlmock.NewRows([]string{""}).AddRow("")) - semVer, _, err = queryVersion(db) + semVer, _, err = queryVersion(t.Context(), db) convey.ShouldBeError(err) convey.So(semVer.String(), convey.ShouldEqual, "0.0.0") mock.ExpectQuery(versionQuery).WillReturnRows(sqlmock.NewRows([]string{""}).AddRow("something")) - semVer, _, err = queryVersion(db) + semVer, _, err = queryVersion(t.Context(), db) convey.ShouldBeNil(err) convey.So(semVer.String(), convey.ShouldEqual, "0.0.0") mock.ExpectQuery(versionQuery).WillReturnRows(sqlmock.NewRows([]string{""}).AddRow("10.1.17-MariaDB")) - semVer, _, err = queryVersion(db) + semVer, _, err = queryVersion(t.Context(), db) convey.ShouldBeNil(err) convey.So(semVer.String(), convey.ShouldEqual, "10.1.17") mock.ExpectQuery(versionQuery).WillReturnRows(sqlmock.NewRows([]string{""}).AddRow("5.7.13-6-log")) - semVer, _, err = queryVersion(db) + semVer, _, err = queryVersion(t.Context(), db) convey.ShouldBeNil(err) convey.So(semVer.String(), convey.ShouldEqual, "5.7.13") mock.ExpectQuery(versionQuery).WillReturnRows(sqlmock.NewRows([]string{""}).AddRow("5.6.30-76.3-56-log")) - semVer, _, err = queryVersion(db) + semVer, _, err = queryVersion(t.Context(), db) convey.ShouldBeNil(err) convey.So(semVer.String(), convey.ShouldEqual, "5.6.30") mock.ExpectQuery(versionQuery).WillReturnRows(sqlmock.NewRows([]string{""}).AddRow("5.5.51-38.1")) - semVer, _, err = queryVersion(db) + semVer, _, err = queryVersion(t.Context(), db) convey.ShouldBeNil(err) convey.So(semVer.String(), convey.ShouldEqual, "5.5.51") }) diff --git a/collector/percona_info_schema_process_list.go b/collector/percona_info_schema_process_list.go index 23718243..7421ccb3 100644 --- a/collector/percona_info_schema_process_list.go +++ b/collector/percona_info_schema_process_list.go @@ -22,18 +22,31 @@ import ( "strings" "github.com/alecthomas/kingpin/v2" + "github.com/blang/semver/v4" "github.com/prometheus/client_golang/prometheus" ) +const ( + processlistInfoSchema = "information_schema" + processlistPerfSchema = "performance_schema" +) + const pInfoSchemaProcesslistQuery = ` SELECT COALESCE(command,''),COALESCE(state,''),count(*),sum(time) - FROM information_schema.processlist + FROM %s.processlist WHERE ID != connection_id() AND TIME >= %d GROUP BY command,state ORDER BY null ` +// MySQL version boundaries for querying perf schema +var ( + v8_0_22 = semver.MustParse("8.0.22") + v5_7_39 = semver.MustParse("5.7.39") + v8_0_0 = semver.MustParse("8.0.0") +) + // Tunable flags. var ( pProcesslistMinTime = kingpin.Flag( @@ -185,10 +198,19 @@ func (PScrapeProcesslist) Version() float64 { // Scrape collects data from database connection and sends it over channel as prometheus metric. func (PScrapeProcesslist) Scrape(ctx context.Context, instance *instance, ch chan<- prometheus.Metric, logger *slog.Logger) error { - processQuery := fmt.Sprintf( - pInfoSchemaProcesslistQuery, - *pProcesslistMinTime, - ) + // Prefer querying performance_schema.processlist instead of information_schema.processlist to avoid negative perf consequences + // Supported by Percona Server/MySQL >=5.7.39 and >=8.0.22 + usePerfSchema := instance.flavor == FlavorMySQL && + instance.isPerformanceSchemaEnabled && + (instance.version.GTE(v8_0_22) || + (instance.version.GTE(v5_7_39) && instance.version.LT(v8_0_0))) + + schema := processlistInfoSchema + if usePerfSchema { + schema = processlistPerfSchema + } + + processQuery := fmt.Sprintf(pInfoSchemaProcesslistQuery, schema, *pProcesslistMinTime) db := instance.getDB() processlistRows, err := db.QueryContext(ctx, processQuery) if err != nil { diff --git a/collector/percona_info_schema_process_list_integration_test.go b/collector/percona_info_schema_process_list_integration_test.go new file mode 100644 index 00000000..0e9ca0c3 --- /dev/null +++ b/collector/percona_info_schema_process_list_integration_test.go @@ -0,0 +1,153 @@ +// Copyright 2018 The Prometheus Authors, 2026 Percona LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +package collector + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/alecthomas/kingpin/v2" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/promslog" + "github.com/testcontainers/testcontainers-go" + tcmariadb "github.com/testcontainers/testcontainers-go/modules/mariadb" + tcmysql "github.com/testcontainers/testcontainers-go/modules/mysql" +) + +func TestPScrapeProcesslist(t *testing.T) { + if testing.Short() { + t.Skip("skipping testcontainers integration test in -short mode") + } + if _, err := kingpin.CommandLine.Parse([]string{}); err != nil { + t.Fatal(err) + } + + cases := []struct { + name string + image string + psEnabled bool + expectedSchema string + }{ + + {"MySQL 5.7.39 + PS on -> perf_schema", "mysql:5.7.39", true, processlistPerfSchema}, + {"MySQL 5.7.39 + PS off -> info_schema", "mysql:5.7.39", false, processlistInfoSchema}, + {"MySQL 5.7.38 + PS on -> info_schema", "mysql:5.7.38", true, processlistInfoSchema}, + {"MySQL 5.7.38 + PS off -> info_schema", "mysql:5.7.38", false, processlistInfoSchema}, + {"MariaDB 10.11 + PS on -> info_schema", "mariadb:10.11", true, processlistInfoSchema}, + {"MariaDB 10.11 + PS off -> info_schema", "mariadb:10.11", false, processlistInfoSchema}, + + // Forward-compatibility coverage + {"MySQL >=8 PS on -> perf_schema", "mysql:8", true, processlistPerfSchema}, + {"MySQL >=9 PS on -> perf_schema", "mysql:9", true, processlistPerfSchema}, + {"MySQL latest PS on -> perf_schema", "mysql:latest", true, processlistPerfSchema}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + ctx := t.Context() + + dsn := startContainerForCase(t, ctx, tc.image, tc.psEnabled) + + db, rec := openRecordingDB(t, dsn) + t.Cleanup(func() { _ = db.Close() }) + + instance, err := newInstanceFromDB(ctx, db) + + if err != nil { + t.Fatalf("failed to create new instance: %v", err) + } + + ch := make(chan prometheus.Metric) + defer close(ch) + go func() { + for range ch { + } + }() + + if err := (PScrapeProcesslist{}).Scrape(ctx, instance, ch, promslog.NewNopLogger()); err != nil { + t.Fatalf("scrape failed: %v; recorded queries: %v", err, rec.recordedQueries()) + } + + wantFragment := fmt.Sprintf("FROM %s.processlist", tc.expectedSchema) + queries := rec.recordedQueries() + for _, q := range queries { + if strings.Contains(strings.Join(strings.Fields(q), " "), wantFragment) { + return + } + } + t.Fatalf("expected a query against %s; got: %v", tc.expectedSchema, queries) + }) + } +} + +// startContainerForCase boots a MySQL or MariaDB container with specified configuration / version +// returns a DSN suitable for go-sql-driver/mysql. +func startContainerForCase(t *testing.T, ctx context.Context, image string, psEnabled bool) string { + t.Helper() + + psFlag := "OFF" + if psEnabled { + psFlag = "ON" + } + cmdArg := fmt.Sprintf("--performance-schema=%s", psFlag) + + switch { + case strings.HasPrefix(image, "mariadb:"): + c, err := tcmariadb.Run(ctx, image, + tcmariadb.WithDatabase("test"), + tcmariadb.WithUsername("test"), + tcmariadb.WithPassword("test"), + testcontainers.WithCmdArgs(cmdArg), + ) + if err != nil { + t.Fatalf("starting %s: %v", image, err) + } + t.Cleanup(func() { + _ = c.Terminate(context.Background()) + }) + dsn, err := c.ConnectionString(ctx) + if err != nil { + t.Fatalf("MariaDB ConnectionString: %v", err) + } + return dsn + + case strings.HasPrefix(image, "mysql:"): + c, err := tcmysql.Run(ctx, image, + tcmysql.WithDatabase("test"), + tcmysql.WithUsername("test"), + tcmysql.WithPassword("test"), + testcontainers.WithCmdArgs(cmdArg), + ) + if err != nil { + t.Fatalf("starting %s: %v", image, err) + } + t.Cleanup(func() { + _ = c.Terminate(context.Background()) + }) + dsn, err := c.ConnectionString(ctx) + if err != nil { + t.Fatalf("MySQL ConnectionString: %v", err) + } + return dsn + default: + t.Fatalf("Unexpected image / db flavor: %s", image) + return "" + } + +} diff --git a/collector/percona_info_schema_process_list_test.go b/collector/percona_info_schema_process_list_test.go new file mode 100644 index 00000000..1ea33f6c --- /dev/null +++ b/collector/percona_info_schema_process_list_test.go @@ -0,0 +1,86 @@ +// Copyright 2018 The Prometheus Authors, 2023 Percona LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collector + +import ( + "fmt" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/alecthomas/kingpin/v2" + "github.com/blang/semver/v4" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/promslog" +) + +func TestPScrapeProcesslistQuerySelection(t *testing.T) { + if _, err := kingpin.CommandLine.Parse([]string{}); err != nil { + t.Fatal(err) + } + + cases := []struct { + name string + flavor string + version semver.Version + perfSchemaEnabled bool + expectedSchema string + }{ + {"MySQL 8.0.22 + PS on -> perf_schema", FlavorMySQL, semver.MustParse("8.0.22"), true, processlistPerfSchema}, + {"MySQL 8.0.30 + PS on -> perf_schema", FlavorMySQL, semver.MustParse("8.0.30"), true, processlistPerfSchema}, + {"MySQL 5.7.39 + PS on -> perf_schema", FlavorMySQL, semver.MustParse("5.7.39"), true, processlistPerfSchema}, + {"MySQL 8.0.22 + PS off -> info_schema", FlavorMySQL, semver.MustParse("8.0.22"), false, processlistInfoSchema}, + {"MySQL 5.7.39 + PS off -> info_schema", FlavorMySQL, semver.MustParse("5.7.39"), false, processlistInfoSchema}, + {"MySQL 8.0.21 -> info_schema", FlavorMySQL, semver.MustParse("8.0.21"), true, processlistInfoSchema}, + {"MySQL 5.7.38 -> info_schema", FlavorMySQL, semver.MustParse("5.7.38"), true, processlistInfoSchema}, + {"MySQL 8.0.0 -> info_schema", FlavorMySQL, semver.MustParse("8.0.0"), true, processlistInfoSchema}, + {"MySQL 5.6.50 -> info_schema", FlavorMySQL, semver.MustParse("5.6.50"), true, processlistInfoSchema}, + {"MariaDB 10.11.0 -> info_schema", FlavorMariaDB, semver.MustParse("10.11.0"), true, processlistInfoSchema}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("error opening a stub database connection: %s", err) + } + defer db.Close() + + inst := &instance{ + db: db, + flavor: tc.flavor, + version: tc.version, + isPerformanceSchemaEnabled: tc.perfSchemaEnabled, + } + + expectedSQL := fmt.Sprintf(pInfoSchemaProcesslistQuery, tc.expectedSchema, 0) + columns := []string{"command", "state", "count", "time"} + mock.ExpectQuery(sanitizeQuery(expectedSQL)). + WillReturnRows(sqlmock.NewRows(columns)) + + ch := make(chan prometheus.Metric) + go func() { + if err := (PScrapeProcesslist{}).Scrape(t.Context(), inst, ch, promslog.NewNopLogger()); err != nil { + t.Errorf("error calling Scrape: %s", err) + } + close(ch) + }() + for range ch { + } + + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("unfulfilled expectations: %s", err) + } + }) + } +} diff --git a/collector/query_recorder_test.go b/collector/query_recorder_test.go new file mode 100644 index 00000000..61c36f92 --- /dev/null +++ b/collector/query_recorder_test.go @@ -0,0 +1,122 @@ +// Copyright 2018 The Prometheus Authors, 2026 Percona LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +package collector + +import ( + "context" + "database/sql" + "database/sql/driver" + "slices" + "sync" + "testing" + + "github.com/go-sql-driver/mysql" +) + +var ( + _ driver.Connector = (*recordingConnector)(nil) + _ driver.Conn = (*recordingConn)(nil) + _ driver.QueryerContext = (*recordingConn)(nil) + _ driver.Pinger = (*recordingConn)(nil) +) + +// queryRecorder collects every SQL string issued via QueryContext on the wrapped +// connection. The string is recorded before delegation, so attempts that fail or +// return driver.ErrSkip are still captured. +type queryRecorder struct { + mu sync.Mutex + queries []string +} + +func (r *queryRecorder) record(q string) { + r.mu.Lock() + r.queries = append(r.queries, q) + r.mu.Unlock() +} + +func (r *queryRecorder) recordedQueries() []string { + r.mu.Lock() + defer r.mu.Unlock() + return slices.Clone(r.queries) +} + +// recordingConnector wraps a driver.Connector and returns connections that +// record every QueryContext call. Integration tests use this to assert which +// SQL the collector issued against a real database without having to mock the +// database itself. +type recordingConnector struct { + inner driver.Connector + recorder *queryRecorder +} + +func (c *recordingConnector) Connect(ctx context.Context) (driver.Conn, error) { + conn, err := c.inner.Connect(ctx) + if err != nil { + return nil, err + } + return &recordingConn{inner: conn, recorder: c.recorder}, nil +} + +func (c *recordingConnector) Driver() driver.Driver { return c.inner.Driver() } + +type recordingConn struct { + inner driver.Conn + recorder *queryRecorder +} + +func (c *recordingConn) Prepare(query string) (driver.Stmt, error) { + return c.inner.Prepare(query) +} + +func (c *recordingConn) Close() error { return c.inner.Close() } + +func (c *recordingConn) Begin() (driver.Tx, error) { return c.inner.Begin() } + +func (c *recordingConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { + c.recorder.record(query) + if qc, ok := c.inner.(driver.QueryerContext); ok { + return qc.QueryContext(ctx, query, args) + } + return nil, driver.ErrSkip +} + +func (c *recordingConn) Ping(ctx context.Context) error { + if p, ok := c.inner.(driver.Pinger); ok { + return p.Ping(ctx) + } + return driver.ErrSkip +} + +// openRecordingDB opens a *sql.DB against dsn whose connections record every +// QueryContext SQL string into the returned recorder. Inspect captures via +// (*queryRecorder).recordedQueries. +func openRecordingDB(t *testing.T, dsn string) (*sql.DB, *queryRecorder) { + t.Helper() + cfg, err := mysql.ParseDSN(dsn) + if err != nil { + t.Fatalf("parse DSN: %v", err) + } + inner, err := mysql.NewConnector(cfg) + if err != nil { + t.Fatalf("mysql.NewConnector: %v", err) + } + rec := &queryRecorder{} + db := sql.OpenDB(&recordingConnector{ + inner: inner, + recorder: rec, + }) + return db, rec +} diff --git a/go.mod b/go.mod index 02f6ac26..ad6603d8 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,9 @@ require ( github.com/prometheus/exporter-toolkit v0.16.0 github.com/smartystreets/goconvey v1.8.1 github.com/stretchr/testify v1.11.1 + github.com/testcontainers/testcontainers-go v0.42.0 + github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 + github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0 github.com/tklauser/go-sysconf v0.3.16 golang.org/x/sys v0.43.0 gopkg.in/ini.v1 v1.67.1 @@ -23,26 +26,65 @@ require ( ) require ( + dario.cat/mergo v1.0.2 // indirect filippo.io/edwards25519 v1.1.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/coreos/go-systemd/v22 v22.7.0 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ebitengine/purego v0.10.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect - github.com/kr/text v0.2.0 // indirect + github.com/klauspost/compress v1.18.5 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.10 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/mdlayher/vsock v1.2.1 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.2.0 // indirect + github.com/moby/moby/api v1.54.1 // indirect + github.com/moby/moby/client v0.4.0 // indirect + github.com/moby/patternmatcher v0.6.1 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/procfs v0.16.1 // indirect + github.com/shirou/gopsutil/v4 v4.26.3 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect github.com/smarty/assertions v1.15.0 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect golang.org/x/crypto v0.49.0 // indirect golang.org/x/net v0.51.0 // indirect diff --git a/go.sum b/go.sum index 05877761..cc8868a1 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,15 @@ +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw= filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= @@ -10,18 +18,49 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -33,26 +72,54 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4= +github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw= +github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/montanaflynn/stats v0.9.0 h1:tsBJ0RXwph9BmAuFoCmqGv6e8xa0MENQ8m0ptKq29mQ= github.com/montanaflynn/stats v0.9.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -63,8 +130,12 @@ github.com/prometheus/exporter-toolkit v0.16.0 h1:xT/j7L2XKF+VJd6B4fpUw6xWabHrSm github.com/prometheus/exporter-toolkit v0.16.0/go.mod h1:d1EL8Z9674xQe/iWhwP2wDyCEoBPbXVeqDbqAUsgJWY= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= +github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= @@ -73,18 +144,42 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 h1:ZfWUJSIDnbNgoLAXMV1fc7lqcxBIX3zdnhwjaVUo7N0= +github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0/go.mod h1:0kV+yHee7zAgp0yccydxjNnHvlC1EOavTLCeg/lnRBY= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0 h1:Yhv1k7vDpyzZePntg5R5Oj4ZMCyWpAfpJeRu1ROsgiU= +github.com/testcontainers/testcontainers-go/modules/mysql v0.42.0/go.mod h1:Z7SCTuiZlghAdRjkv3Ir0iXJKC2T2avbtxLR0DRe+ng= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= @@ -97,12 +192,18 @@ golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -115,3 +216,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/percona/tests/Makefile b/percona/tests/Makefile index 3cb34bf5..02049ffd 100644 --- a/percona/tests/Makefile +++ b/percona/tests/Makefile @@ -40,9 +40,9 @@ prepare-base-exporter: tar -xf assets/mysqld_exporter_percona.tar.xz -C assets/ start-mysql-db: - docker-compose -f assets/mysql-compose.yml up -d --force-recreate --renew-anon-volumes --remove-orphans + docker compose -f assets/mysql-compose.yml up -d --force-recreate --renew-anon-volumes --remove-orphans stop-mysql-db: - docker-compose -f assets/mysql-compose.yml down + docker compose -f assets/mysql-compose.yml down prepare-env-from-repo: prepare-exporter-from-repo prepare-base-exporter start-mysql-db diff --git a/percona/tests/assets/test.exporter-flags.txt b/percona/tests/assets/test.exporter-flags.txt index 508c5f4a..e2a01df3 100644 --- a/percona/tests/assets/test.exporter-flags.txt +++ b/percona/tests/assets/test.exporter-flags.txt @@ -23,7 +23,6 @@ --exporter.max-idle-conns=3 --exporter.max-open-conns=3 --exporter.conn-max-lifetime=55s ---exporter.global-conn-pool --collect.info_schema.innodb_tablespaces --collect.auto_increment.columns --collect.info_schema.tables diff --git a/percona/tests/env_prepare_test.go b/percona/tests/env_prepare_test.go index 5df1f26a..f2ecaf18 100644 --- a/percona/tests/env_prepare_test.go +++ b/percona/tests/env_prepare_test.go @@ -51,7 +51,7 @@ func extractExporter(gzipStream io.Reader, fileName string) { case tar.TypeDir: continue case tar.TypeReg: - if strings.HasSuffix(header.Name, "postgres_exporter") { + if strings.HasSuffix(header.Name, "mysqld_exporter") { outFile, err := os.Create(fileName) if err != nil { log.Fatalf("ExtractTarGz: Create() failed: %s", err.Error()) diff --git a/percona/tests/utils_test.go b/percona/tests/utils_test.go index 73e7c165..2bd5eed8 100644 --- a/percona/tests/utils_test.go +++ b/percona/tests/utils_test.go @@ -53,21 +53,27 @@ func launchExporter(fileName string) (cmd *exec.Cmd, port int, collectOutput fun return nil, 0, nil, fmt.Errorf("Failed to find free port in range [%d..%d]", portRangeStart, portRangeEnd) } - linesStr := string(lines) + linesStr := strings.TrimSpace(string(lines)) linesStr += fmt.Sprintf("\n--web.listen-address=127.0.0.1:%d", port) + linesStr += fmt.Sprintf("\n--mysqld.address=%s:%d", mysqlHost, mysqlPort) + linesStr += fmt.Sprintf("\n--mysqld.username=%s", mysqlUser) + linesStr += "\n--config.my-cnf=/dev/null" absolutePath, _ := filepath.Abs("custom-queries") linesStr += fmt.Sprintf("\n--collect.custom_query.hr.directory=%s/high-resolution", absolutePath) linesStr += fmt.Sprintf("\n--collect.custom_query.mr.directory=%s/medium-resolution", absolutePath) linesStr += fmt.Sprintf("\n--collect.custom_query.lr.directory=%s/low-resolution", absolutePath) - linesArr := strings.Split(linesStr, "\n") - - dsn := fmt.Sprintf("DATA_SOURCE_NAME=%s:%s@(%s:%d)/", mysqlUser, mysqlPassword, mysqlHost, mysqlPort) + linesArr := make([]string, 0) + for _, l := range strings.Split(linesStr, "\n") { + if l = strings.TrimSpace(l); l != "" { + linesArr = append(linesArr, l) + } + } cmd = exec.Command(fileName, linesArr...) cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, dsn) + cmd.Env = append(cmd.Env, "MYSQLD_EXPORTER_PASSWORD="+mysqlPassword) var outBuffer, errorBuffer bytes.Buffer cmd.Stdout = &outBuffer