Skip to content
This repository was archived by the owner on Mar 13, 2024. It is now read-only.

Commit 045ae96

Browse files
committed
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.
1 parent a95fad8 commit 045ae96

File tree

4 files changed

+118
-22
lines changed

4 files changed

+118
-22
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.build
22
postgres_exporter
3+
postgres_exporter_integration_test
34
*.tar.gz
45
*.test
56
*-stamp

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ all: vet test postgres_exporter
99
postgres_exporter: $(GO_SRC)
1010
CGO_ENABLED=0 go build -a -ldflags "-extldflags '-static' -X main.Version=git:$(shell git rev-parse HEAD)" -o postgres_exporter .
1111

12+
postgres_exporter_integration_test: $(GO_SRC)
13+
CGO_ENABLED=0 go test -c -tags integration \
14+
-a -ldflags "-extldflags '-static' -X main.Version=git:$(shell git rev-parse HEAD)" -o postgres_exporter_integration_test .
15+
1216
# Take a go build and turn it into a minimal container
1317
docker: postgres_exporter
1418
docker build -t $(CONTAINER_NAME) .
@@ -19,8 +23,8 @@ vet:
1923
test:
2024
go test -v .
2125

22-
test-integration: postgres_exporter
23-
tests/test-smoke ./postgres_exporter
26+
test-integration: postgres_exporter postgres_exporter_integration_test
27+
tests/test-smoke ./postgres_exporter ./postgres_exporter_integration_test
2428

2529
# Do a self-contained docker build - we pull the official upstream container
2630
# and do a self-contained build.

postgres_exporter.go

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -592,9 +592,11 @@ func newDesc(subsystem, name, help string) *prometheus.Desc {
592592

593593
// Query the SHOW variables from the query map
594594
// TODO: make this more functional
595-
func (e *Exporter) queryShowVariables(ch chan<- prometheus.Metric, db *sql.DB) {
595+
func queryShowVariables(ch chan<- prometheus.Metric, db *sql.DB, variableMap map[string]MetricMapNamespace) []error {
596596
log.Debugln("Querying SHOW variables")
597-
for _, mapping := range e.variableMap {
597+
nonFatalErrors := []error{}
598+
599+
for _, mapping := range variableMap {
598600
for columnName, columnMapping := range mapping.columnMappings {
599601
// Check for a discard request on this value
600602
if columnMapping.discard {
@@ -607,23 +609,26 @@ func (e *Exporter) queryShowVariables(ch chan<- prometheus.Metric, db *sql.DB) {
607609
var val interface{}
608610
err := row.Scan(&val)
609611
if err != nil {
610-
log.Errorln("Error scanning runtime variable:", columnName, err)
612+
nonFatalErrors = append(nonFatalErrors, errors.New(fmt.Sprintln("Error scanning runtime variable:", columnName, err)))
611613
continue
612614
}
613615

614616
fval, ok := columnMapping.conversion(val)
615617
if !ok {
616-
e.error.Set(1)
617-
log.Errorln("Unexpected error parsing column: ", namespace, columnName, val)
618+
nonFatalErrors = append(nonFatalErrors, errors.New(fmt.Sprintln("Unexpected error parsing column: ", namespace, columnName, val)))
618619
continue
619620
}
620621

621622
ch <- prometheus.MustNewConstMetric(columnMapping.desc, columnMapping.vtype, fval)
622623
}
623624
}
625+
626+
return nonFatalErrors
624627
}
625628

626-
func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace string, mapping MetricMapNamespace) error {
629+
// Query within a namespace mapping and emit metrics. Returns fatal errors if
630+
// the scrape fails, and a slice of errors if they were non-fatal.
631+
func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace string, mapping MetricMapNamespace) ([]error, error) {
627632
query, er := queryOverrides[namespace]
628633
if er == false {
629634
query = fmt.Sprintf("SELECT * FROM %s;", namespace)
@@ -632,14 +637,14 @@ func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace st
632637
// Don't fail on a bad scrape of one metric
633638
rows, err := db.Query(query)
634639
if err != nil {
635-
return errors.New(fmt.Sprintln("Error running query on database: ", namespace, err))
640+
return []error{}, errors.New(fmt.Sprintln("Error running query on database: ", namespace, err))
636641
}
637642
defer rows.Close()
638643

639644
var columnNames []string
640645
columnNames, err = rows.Columns()
641646
if err != nil {
642-
return errors.New(fmt.Sprintln("Error retrieving column list for: ", namespace, err))
647+
return []error{}, errors.New(fmt.Sprintln("Error retrieving column list for: ", namespace, err))
643648
}
644649

645650
// Make a lookup map for the column indices
@@ -654,10 +659,12 @@ func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace st
654659
scanArgs[i] = &columnData[i]
655660
}
656661

662+
nonfatalErrors := []error{}
663+
657664
for rows.Next() {
658665
err = rows.Scan(scanArgs...)
659666
if err != nil {
660-
return errors.New(fmt.Sprintln("Error retrieving rows:", namespace, err))
667+
return []error{}, errors.New(fmt.Sprintln("Error retrieving rows:", namespace, err))
661668
}
662669

663670
// Get the label values for this row
@@ -678,7 +685,7 @@ func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace st
678685

679686
value, ok := dbToFloat64(columnData[idx])
680687
if !ok {
681-
log.Errorln("Unexpected error parsing column: ", namespace, columnName, columnData[idx])
688+
nonfatalErrors = append(nonfatalErrors, errors.New(fmt.Sprintln("Unexpected error parsing column: ", namespace, columnName, columnData[idx])))
682689
continue
683690
}
684691

@@ -692,15 +699,40 @@ func queryNamespaceMapping(ch chan<- prometheus.Metric, db *sql.DB, namespace st
692699
// unexpected anyway.
693700
value, ok := dbToFloat64(columnData[idx])
694701
if !ok {
695-
log.Warnln("Unparseable column type - discarding: ", namespace, columnName, err)
702+
nonfatalErrors = append(nonfatalErrors, errors.New(fmt.Sprintln("Unparseable column type - discarding: ", namespace, columnName, err)))
696703
continue
697704
}
698705

699706
ch <- prometheus.MustNewConstMetric(desc, prometheus.UntypedValue, value, labels...)
700707
}
701708
}
702709
}
703-
return nil
710+
return nonfatalErrors, nil
711+
}
712+
713+
// Iterate through all the namespace mappings in the exporter and run their
714+
// queries.
715+
func queryNamespaceMappings(ch chan<- prometheus.Metric, db *sql.DB, metricMap map[string]MetricMapNamespace) map[string]error {
716+
// Return a map of namespace -> errors
717+
namespaceErrors := make(map[string]error)
718+
719+
for namespace, mapping := range metricMap {
720+
log.Debugln("Querying namespace: ", namespace)
721+
nonFatalErrors, err := queryNamespaceMapping(ch, db, namespace, mapping)
722+
// Serious error - a namespace disappeard
723+
if err != nil {
724+
namespaceErrors[namespace] = err
725+
log.Infoln(err)
726+
}
727+
// Non-serious errors - likely version or parsing problems.
728+
if len(nonFatalErrors) > 0 {
729+
for _, err := range nonFatalErrors {
730+
log.Infoln(err.Error())
731+
}
732+
}
733+
}
734+
735+
return namespaceErrors
704736
}
705737

706738
func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
@@ -734,15 +766,14 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
734766
ch <- prometheus.MustNewConstMetric(versionDesc, prometheus.UntypedValue, 1, versionString, shortVersion)
735767

736768
// Handle querying the show variables
737-
e.queryShowVariables(ch, db)
769+
nonFatalErrors := queryShowVariables(ch, db, e.variableMap)
770+
if len(nonFatalErrors) > 0 {
771+
e.error.Set(1)
772+
}
738773

739-
for namespace, mapping := range e.metricMap {
740-
log.Debugln("Querying namespace: ", namespace)
741-
err = queryNamespaceMapping(ch, db, namespace, mapping)
742-
if err != nil {
743-
log.Infoln(err)
744-
e.error.Set(1)
745-
}
774+
errMap := queryNamespaceMappings(ch, db, e.metricMap)
775+
if len(errMap) > 0 {
776+
e.error.Set(1)
746777
}
747778
}
748779

postgres_exporter_integration_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// These are specialized integration tests. We only build them when we're doing
2+
// a lot of additional work to keep the external docker environment they require
3+
// working.
4+
// +build integration
5+
6+
package main
7+
8+
import (
9+
"os"
10+
"testing"
11+
12+
. "gopkg.in/check.v1"
13+
14+
"github.com/prometheus/client_golang/prometheus"
15+
"database/sql"
16+
_ "github.com/lib/pq"
17+
)
18+
19+
// Hook up gocheck into the "go test" runner.
20+
func Test(t *testing.T) { TestingT(t) }
21+
22+
type IntegrationSuite struct{
23+
e *Exporter
24+
}
25+
26+
var _ = Suite(&IntegrationSuite{})
27+
28+
func (s *IntegrationSuite) SetUpSuite(c *C) {
29+
dsn := os.Getenv("DATA_SOURCE_NAME")
30+
c.Assert(dsn, Not(Equals), "")
31+
32+
exporter := NewExporter(dsn)
33+
c.Assert(exporter, NotNil)
34+
// Assign the exporter to the suite
35+
s.e = exporter
36+
37+
prometheus.MustRegister(exporter)
38+
}
39+
40+
func (s *IntegrationSuite) TestAllNamespacesReturnResults(c *C) {
41+
// Setup a dummy channel to consume metrics
42+
ch := make(chan prometheus.Metric, 100)
43+
go func() {
44+
for _ = range ch {}
45+
}()
46+
47+
// Open a database connection
48+
db, err := sql.Open("postgres", s.e.dsn)
49+
c.Assert(db, NotNil)
50+
c.Assert(err, IsNil)
51+
defer db.Close()
52+
53+
// Check the show variables work
54+
nonFatalErrors := queryShowVariables(ch, db, s.e.variableMap)
55+
c.Check(len(nonFatalErrors), Equals, 0)
56+
57+
// This should never happen in our test cases.
58+
errMap := queryNamespaceMappings(ch, db, s.e.metricMap)
59+
c.Check(len(errMap), Equals, 0)
60+
}

0 commit comments

Comments
 (0)