mirror of
https://github.com/prometheus-community/postgres_exporter
synced 2025-04-21 22:45:26 +00:00
Implement the compileable source code of the basic integration test suite.
This is the first step in moving the integration test suite to a more reliable Golang based one.
This commit is contained in:
parent
a95fad8d1e
commit
045ae96430
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
.build
|
.build
|
||||||
postgres_exporter
|
postgres_exporter
|
||||||
|
postgres_exporter_integration_test
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.test
|
*.test
|
||||||
*-stamp
|
*-stamp
|
||||||
|
8
Makefile
8
Makefile
@ -9,6 +9,10 @@ all: vet test postgres_exporter
|
|||||||
postgres_exporter: $(GO_SRC)
|
postgres_exporter: $(GO_SRC)
|
||||||
CGO_ENABLED=0 go build -a -ldflags "-extldflags '-static' -X main.Version=git:$(shell git rev-parse HEAD)" -o postgres_exporter .
|
CGO_ENABLED=0 go build -a -ldflags "-extldflags '-static' -X main.Version=git:$(shell git rev-parse HEAD)" -o postgres_exporter .
|
||||||
|
|
||||||
|
postgres_exporter_integration_test: $(GO_SRC)
|
||||||
|
CGO_ENABLED=0 go test -c -tags integration \
|
||||||
|
-a -ldflags "-extldflags '-static' -X main.Version=git:$(shell git rev-parse HEAD)" -o postgres_exporter_integration_test .
|
||||||
|
|
||||||
# Take a go build and turn it into a minimal container
|
# Take a go build and turn it into a minimal container
|
||||||
docker: postgres_exporter
|
docker: postgres_exporter
|
||||||
docker build -t $(CONTAINER_NAME) .
|
docker build -t $(CONTAINER_NAME) .
|
||||||
@ -19,8 +23,8 @@ vet:
|
|||||||
test:
|
test:
|
||||||
go test -v .
|
go test -v .
|
||||||
|
|
||||||
test-integration: postgres_exporter
|
test-integration: postgres_exporter postgres_exporter_integration_test
|
||||||
tests/test-smoke ./postgres_exporter
|
tests/test-smoke ./postgres_exporter ./postgres_exporter_integration_test
|
||||||
|
|
||||||
# Do a self-contained docker build - we pull the official upstream container
|
# Do a self-contained docker build - we pull the official upstream container
|
||||||
# and do a self-contained build.
|
# and do a self-contained build.
|
||||||
|
@ -592,9 +592,11 @@ func newDesc(subsystem, name, help string) *prometheus.Desc {
|
|||||||
|
|
||||||
// Query the SHOW variables from the query map
|
// Query the SHOW variables from the query map
|
||||||
// TODO: make this more functional
|
// TODO: make this more functional
|
||||||
func (e *Exporter) queryShowVariables(ch chan<- prometheus.Metric, db *sql.DB) {
|
func queryShowVariables(ch chan<- prometheus.Metric, db *sql.DB, variableMap map[string]MetricMapNamespace) []error {
|
||||||
log.Debugln("Querying SHOW variables")
|
log.Debugln("Querying SHOW variables")
|
||||||
for _, mapping := range e.variableMap {
|
nonFatalErrors := []error{}
|
||||||
|
|
||||||
|
for _, mapping := range variableMap {
|
||||||
for columnName, columnMapping := range mapping.columnMappings {
|
for columnName, columnMapping := range mapping.columnMappings {
|
||||||
// Check for a discard request on this value
|
// Check for a discard request on this value
|
||||||
if columnMapping.discard {
|
if columnMapping.discard {
|
||||||
@ -607,23 +609,26 @@ func (e *Exporter) queryShowVariables(ch chan<- prometheus.Metric, db *sql.DB) {
|
|||||||
var val interface{}
|
var val interface{}
|
||||||
err := row.Scan(&val)
|
err := row.Scan(&val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("Error scanning runtime variable:", columnName, err)
|
nonFatalErrors = append(nonFatalErrors, errors.New(fmt.Sprintln("Error scanning runtime variable:", columnName, err)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fval, ok := columnMapping.conversion(val)
|
fval, ok := columnMapping.conversion(val)
|
||||||
if !ok {
|
if !ok {
|
||||||
e.error.Set(1)
|
nonFatalErrors = append(nonFatalErrors, errors.New(fmt.Sprintln("Unexpected error parsing column: ", namespace, columnName, val)))
|
||||||
log.Errorln("Unexpected error parsing column: ", namespace, columnName, val)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(columnMapping.desc, columnMapping.vtype, fval)
|
ch <- prometheus.MustNewConstMetric(columnMapping.desc, columnMapping.vtype, fval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nonFatalErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace string, mapping MetricMapNamespace) error {
|
// Query within a namespace mapping and emit metrics. Returns fatal errors if
|
||||||
|
// the scrape fails, and a slice of errors if they were non-fatal.
|
||||||
|
func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace string, mapping MetricMapNamespace) ([]error, error) {
|
||||||
query, er := queryOverrides[namespace]
|
query, er := queryOverrides[namespace]
|
||||||
if er == false {
|
if er == false {
|
||||||
query = fmt.Sprintf("SELECT * FROM %s;", namespace)
|
query = fmt.Sprintf("SELECT * FROM %s;", namespace)
|
||||||
@ -632,14 +637,14 @@ func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace st
|
|||||||
// Don't fail on a bad scrape of one metric
|
// Don't fail on a bad scrape of one metric
|
||||||
rows, err := db.Query(query)
|
rows, err := db.Query(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintln("Error running query on database: ", namespace, err))
|
return []error{}, errors.New(fmt.Sprintln("Error running query on database: ", namespace, err))
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var columnNames []string
|
var columnNames []string
|
||||||
columnNames, err = rows.Columns()
|
columnNames, err = rows.Columns()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintln("Error retrieving column list for: ", namespace, err))
|
return []error{}, errors.New(fmt.Sprintln("Error retrieving column list for: ", namespace, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a lookup map for the column indices
|
// Make a lookup map for the column indices
|
||||||
@ -654,10 +659,12 @@ func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace st
|
|||||||
scanArgs[i] = &columnData[i]
|
scanArgs[i] = &columnData[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonfatalErrors := []error{}
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err = rows.Scan(scanArgs...)
|
err = rows.Scan(scanArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(fmt.Sprintln("Error retrieving rows:", namespace, err))
|
return []error{}, errors.New(fmt.Sprintln("Error retrieving rows:", namespace, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the label values for this row
|
// Get the label values for this row
|
||||||
@ -678,7 +685,7 @@ func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace st
|
|||||||
|
|
||||||
value, ok := dbToFloat64(columnData[idx])
|
value, ok := dbToFloat64(columnData[idx])
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Errorln("Unexpected error parsing column: ", namespace, columnName, columnData[idx])
|
nonfatalErrors = append(nonfatalErrors, errors.New(fmt.Sprintln("Unexpected error parsing column: ", namespace, columnName, columnData[idx])))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -692,7 +699,7 @@ func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace st
|
|||||||
// unexpected anyway.
|
// unexpected anyway.
|
||||||
value, ok := dbToFloat64(columnData[idx])
|
value, ok := dbToFloat64(columnData[idx])
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warnln("Unparseable column type - discarding: ", namespace, columnName, err)
|
nonfatalErrors = append(nonfatalErrors, errors.New(fmt.Sprintln("Unparseable column type - discarding: ", namespace, columnName, err)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,7 +707,32 @@ func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nonfatalErrors, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through all the namespace mappings in the exporter and run their
|
||||||
|
// queries.
|
||||||
|
func queryNamespaceMappings(ch chan<- prometheus.Metric, db *sql.DB, metricMap map[string]MetricMapNamespace) map[string]error {
|
||||||
|
// Return a map of namespace -> errors
|
||||||
|
namespaceErrors := make(map[string]error)
|
||||||
|
|
||||||
|
for namespace, mapping := range metricMap {
|
||||||
|
log.Debugln("Querying namespace: ", namespace)
|
||||||
|
nonFatalErrors, err := queryNamespaceMapping(ch, db, namespace, mapping)
|
||||||
|
// Serious error - a namespace disappeard
|
||||||
|
if err != nil {
|
||||||
|
namespaceErrors[namespace] = err
|
||||||
|
log.Infoln(err)
|
||||||
|
}
|
||||||
|
// Non-serious errors - likely version or parsing problems.
|
||||||
|
if len(nonFatalErrors) > 0 {
|
||||||
|
for _, err := range nonFatalErrors {
|
||||||
|
log.Infoln(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return namespaceErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
|
func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
|
||||||
@ -734,15 +766,14 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
|
|||||||
ch <- prometheus.MustNewConstMetric(versionDesc, prometheus.UntypedValue, 1, versionString, shortVersion)
|
ch <- prometheus.MustNewConstMetric(versionDesc, prometheus.UntypedValue, 1, versionString, shortVersion)
|
||||||
|
|
||||||
// Handle querying the show variables
|
// Handle querying the show variables
|
||||||
e.queryShowVariables(ch, db)
|
nonFatalErrors := queryShowVariables(ch, db, e.variableMap)
|
||||||
|
if len(nonFatalErrors) > 0 {
|
||||||
|
e.error.Set(1)
|
||||||
|
}
|
||||||
|
|
||||||
for namespace, mapping := range e.metricMap {
|
errMap := queryNamespaceMappings(ch, db, e.metricMap)
|
||||||
log.Debugln("Querying namespace: ", namespace)
|
if len(errMap) > 0 {
|
||||||
err = queryNamespaceMapping(ch, db, namespace, mapping)
|
e.error.Set(1)
|
||||||
if err != nil {
|
|
||||||
log.Infoln(err)
|
|
||||||
e.error.Set(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
60
postgres_exporter_integration_test.go
Normal file
60
postgres_exporter_integration_test.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// These are specialized integration tests. We only build them when we're doing
|
||||||
|
// a lot of additional work to keep the external docker environment they require
|
||||||
|
// working.
|
||||||
|
// +build integration
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"database/sql"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hook up gocheck into the "go test" runner.
|
||||||
|
func Test(t *testing.T) { TestingT(t) }
|
||||||
|
|
||||||
|
type IntegrationSuite struct{
|
||||||
|
e *Exporter
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Suite(&IntegrationSuite{})
|
||||||
|
|
||||||
|
func (s *IntegrationSuite) SetUpSuite(c *C) {
|
||||||
|
dsn := os.Getenv("DATA_SOURCE_NAME")
|
||||||
|
c.Assert(dsn, Not(Equals), "")
|
||||||
|
|
||||||
|
exporter := NewExporter(dsn)
|
||||||
|
c.Assert(exporter, NotNil)
|
||||||
|
// Assign the exporter to the suite
|
||||||
|
s.e = exporter
|
||||||
|
|
||||||
|
prometheus.MustRegister(exporter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IntegrationSuite) TestAllNamespacesReturnResults(c *C) {
|
||||||
|
// Setup a dummy channel to consume metrics
|
||||||
|
ch := make(chan prometheus.Metric, 100)
|
||||||
|
go func() {
|
||||||
|
for _ = range ch {}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Open a database connection
|
||||||
|
db, err := sql.Open("postgres", s.e.dsn)
|
||||||
|
c.Assert(db, NotNil)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Check the show variables work
|
||||||
|
nonFatalErrors := queryShowVariables(ch, db, s.e.variableMap)
|
||||||
|
c.Check(len(nonFatalErrors), Equals, 0)
|
||||||
|
|
||||||
|
// This should never happen in our test cases.
|
||||||
|
errMap := queryNamespaceMappings(ch, db, s.e.metricMap)
|
||||||
|
c.Check(len(errMap), Equals, 0)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user