diff --git a/internal/collector/pgbouncer_settings.go b/internal/collector/pgbouncer_settings.go index 5260b11..7d75053 100644 --- a/internal/collector/pgbouncer_settings.go +++ b/internal/collector/pgbouncer_settings.go @@ -2,6 +2,7 @@ package collector import ( "bufio" + "context" "fmt" "github.com/prometheus/client_golang/prometheus" "github.com/weaponry/pgscv/internal/log" @@ -9,16 +10,18 @@ import ( "github.com/weaponry/pgscv/internal/store" "os" "path/filepath" + "regexp" "strconv" "strings" ) const ( - // admin console query used for retrieving settings settingsQuery = "SHOW CONFIG" + versionQuery = "SHOW VERSION" ) type pgbouncerSettingsCollector struct { + version typedDesc settings typedDesc dbSettings typedDesc poolSize typedDesc @@ -28,6 +31,12 @@ type pgbouncerSettingsCollector struct { // For details see https://www.pgbouncer.org/usage.html#show-config. func NewPgbouncerSettingsCollector(constLabels labels, settings model.CollectorSettings) (Collector, error) { return &pgbouncerSettingsCollector{ + version: newBuiltinTypedDesc( + descOpts{"pgbouncer", "", "version", "Numeric representation of Pgbouncer version.", 0}, + prometheus.GaugeValue, + []string{"version"}, constLabels, + settings.Filters, + ), settings: newBuiltinTypedDesc( descOpts{"pgbouncer", "service", "settings_info", "Labeled information about Pgbouncer configuration settings.", 0}, prometheus.GaugeValue, @@ -57,6 +66,13 @@ func (c *pgbouncerSettingsCollector) Update(config Config, ch chan<- prometheus. } defer conn.Close() + version, versionStr, err := queryPgbouncerVersion(conn) + if err != nil { + return err + } + + ch <- c.version.newConstMetric(float64(version), versionStr) + res, err := conn.Query(settingsQuery) if err != nil { return err @@ -100,6 +116,25 @@ func (c *pgbouncerSettingsCollector) Update(config Config, ch chan<- prometheus. return nil } +// queryPgbouncerVersion queries version info from Pgbouncer and return numeric and string version representation. +func queryPgbouncerVersion(conn *store.DB) (int, string, error) { + var versionStr string + err := conn.Conn().QueryRow(context.Background(), versionQuery).Scan(&versionStr) + if err != nil { + return 0, "", err + } + + re := regexp.MustCompile(`\d+\.\d+\.\d+`) + versionStr = re.FindString(versionStr) + + version, err := semverStringToInt(versionStr) + if err != nil { + return 0, "", fmt.Errorf("parse version string '%s' failed: %s", versionStr, err) + } + + return version, versionStr, nil +} + // parsePgbouncerSettings parses content of 'SHOW CONFIG' and return map with parsed settings. func parsePgbouncerSettings(r *model.PGResult) map[string]string { log.Debug("parse pgbouncer settings") diff --git a/internal/collector/pgbouncer_settings_test.go b/internal/collector/pgbouncer_settings_test.go index ffea98d..85f6e2d 100644 --- a/internal/collector/pgbouncer_settings_test.go +++ b/internal/collector/pgbouncer_settings_test.go @@ -5,12 +5,14 @@ import ( "github.com/jackc/pgproto3/v2" "github.com/stretchr/testify/assert" "github.com/weaponry/pgscv/internal/model" + "github.com/weaponry/pgscv/internal/store" "testing" ) func TestPgbouncerSettingsCollector_Update(t *testing.T) { var input = pipelineInput{ required: []string{ + "pgbouncer_version", "pgbouncer_service_settings_info", "pgbouncer_service_database_settings_info", "pgbouncer_service_database_pool_size", @@ -22,6 +24,15 @@ func TestPgbouncerSettingsCollector_Update(t *testing.T) { pipeline(t, input) } +func Test_queryPgbouncerVersion(t *testing.T) { + db := store.NewTestPgbouncer(t) + + str, num, err := queryPgbouncerVersion(db) + assert.NoError(t, err) + assert.NotEqual(t, "", str) + assert.NotEqual(t, 0, num) +} + func Test_parsePgbouncerSettings(t *testing.T) { var testCases = []struct { name string diff --git a/internal/store/testing.go b/internal/store/testing.go index e39af30..ba33b12 100644 --- a/internal/store/testing.go +++ b/internal/store/testing.go @@ -5,10 +5,17 @@ import ( "testing" ) -const TestPostgresConnStr = "host=127.0.0.1 dbname=pgscv_fixtures user=pgscv sslmode=disable" +const TestPostgresConnStr = "host=127.0.0.1 port=5432 user=pgscv dbname=pgscv_fixtures sslmode=disable" +const TestPgbouncerConnStr = "host=127.0.0.1 port=6432 user=pgscv dbname=pgbouncer sslmode=disable" func NewTest(t *testing.T) *DB { db, err := New(TestPostgresConnStr) assert.NoError(t, err) return db } + +func NewTestPgbouncer(t *testing.T) *DB { + db, err := New(TestPgbouncerConnStr) + assert.NoError(t, err) + return db +}