Skip to content
122 changes: 122 additions & 0 deletions collector/pg_extensions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package collector

import (
"context"
"database/sql"
"github.com/go-kit/log"
"github.com/prometheus/client_golang/prometheus"
"strconv"
)

func init() {
registerCollector("extensions", defaultEnabled, NewExtensionsCollector)
}

var pgExtensions = map[string]*prometheus.Desc{
"pg_available_extensions": prometheus.NewDesc(
"pg_available_extensions",
"Extensions that are available for installation",
[]string{
"name",
"default_version",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does it mean default? used? I think we need a better name here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these lablels are copies of column names from the table from which we fetch these metrics.

I believe it means if you install this extensions and not specify the version this default version will be used.

"installed_version",
},
prometheus.Labels{},
),
"pg_extensions": prometheus.NewDesc(
"pg_extensions",
"Installed extensions",
[]string{
"name",
"relocatable",
"version",
},
prometheus.Labels{},
),
}

type ExtensionsCollector struct {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about checking that struct implements interface?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added it

logger log.Logger
}

func NewExtensionsCollector(logger log.Logger) (Collector, error) {
return &ExtensionsCollector{logger: logger}, nil
}

func (e *ExtensionsCollector) Update(ctx context.Context, server *server, ch chan<- prometheus.Metric) error {
db, err := server.GetDB()
if err != nil {
return err
}

err = e.scrapeAvailableExtensions(ctx, db, ch)
if err != nil {
return err
}

err = e.scrapeInstalledExtensions(ctx, db, ch)
if err != nil {
return err
}

return nil
}

func (e *ExtensionsCollector) scrapeInstalledExtensions(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric) error {
rowsExtensions, err := db.QueryContext(ctx, `SELECT extname, extrelocatable, extversion FROM pg_extension`)

if err != nil {
return err
}
defer rowsExtensions.Close()

for rowsExtensions.Next() {
var extname string
var extrelocatable bool
var extversion string
if err := rowsExtensions.Scan(&extname, &extrelocatable, &extversion); err != nil {
return err
}

ch <- prometheus.MustNewConstMetric(
pgExtensions["pg_extensions"],
prometheus.GaugeValue,
1,
extname,
strconv.FormatBool(extrelocatable),
extversion,
)
}

return nil
}

func (e *ExtensionsCollector) scrapeAvailableExtensions(ctx context.Context, db *sql.DB, ch chan<- prometheus.Metric) error {
rows, err := db.QueryContext(ctx, `SELECT name, default_version, installed_version FROM pg_available_extensions`)
if err != nil {
return err
}
defer rows.Close()

for rows.Next() {
var name sql.NullString
var defaultVersion sql.NullString
var installedVersion sql.NullString
if err := rows.Scan(&name, &defaultVersion, &installedVersion); err != nil {
return err
}

ch <- prometheus.MustNewConstMetric(
pgExtensions["pg_available_extensions"],
prometheus.GaugeValue,
1,
name.String,
defaultVersion.String,
installedVersion.String,
)
}

return nil
}

var _ = (Collector)(&ExtensionsCollector{})