Skip to content

Commit 2ab8f10

Browse files
tamalsahawrouesnel
authored andcommitted
Refactor exporter so that it can be used as a library.
1 parent 4117fb2 commit 2ab8f10

File tree

7 files changed

+98
-85
lines changed

7 files changed

+98
-85
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ fmt: tools
8989
postgres_exporter_integration_test: $(GO_SRC)
9090
CGO_ENABLED=0 go test -c -tags integration \
9191
-a -ldflags "-extldflags '-static' -X main.Version=$(VERSION)" \
92-
-o postgres_exporter_integration_test -cover -covermode count .
92+
-o postgres_exporter_integration_test -cover -covermode count ./collector/...
9393

9494
test: tools
9595
@mkdir -p $(COVERDIR)

cmd/postgres_exporter/pg_setting.go renamed to collector/pg_setting.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package main
1+
package collector
22

33
import (
44
"database/sql"

cmd/postgres_exporter/pg_setting_test.go renamed to collector/pg_setting_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// +build !integration
22

3-
package main
3+
package collector
44

55
import (
66
dto "github.com/prometheus/client_model/go"

cmd/postgres_exporter/postgres_exporter.go renamed to collector/postgres_exporter.go

Lines changed: 14 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,25 @@
1-
package main
1+
package collector
22

33
import (
4+
"crypto/sha256"
45
"database/sql"
56
"errors"
67
"fmt"
78
"io/ioutil"
89
"math"
9-
"net/http"
1010
"net/url"
1111
"os"
1212
"regexp"
13-
"runtime"
1413
"strconv"
1514
"strings"
1615
"sync"
1716
"time"
1817

19-
"gopkg.in/alecthomas/kingpin.v2"
20-
"gopkg.in/yaml.v2"
21-
22-
"crypto/sha256"
2318
"github.com/blang/semver"
2419
_ "github.com/lib/pq"
2520
"github.com/prometheus/client_golang/prometheus"
26-
"github.com/prometheus/client_golang/prometheus/promhttp"
2721
"github.com/prometheus/common/log"
28-
)
29-
30-
// Version is set during build to the git describe version
31-
// (semantic version)-(commitish) form.
32-
var Version = "0.0.1"
33-
34-
var (
35-
listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9187").OverrideDefaultFromEnvar("PG_EXPORTER_WEB_LISTEN_ADDRESS").String()
36-
metricPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").OverrideDefaultFromEnvar("PG_EXPORTER_WEB_TELEMETRY_PATH").String()
37-
queriesPath = kingpin.Flag("extend.query-path", "Path to custom queries to run.").Default("").OverrideDefaultFromEnvar("PG_EXPORTER_EXTEND_QUERY_PATH").String()
38-
onlyDumpMaps = kingpin.Flag("dumpmaps", "Do not run, simply dump the maps.").Bool()
22+
"gopkg.in/yaml.v2"
3923
)
4024

4125
// Metric name parts.
@@ -124,7 +108,7 @@ type MetricMap struct {
124108
}
125109

126110
// TODO: revisit this with the semver system
127-
func dumpMaps() {
111+
func DumpMaps() {
128112
// TODO: make this function part of the exporter
129113
for name, cmap := range builtinMetricMaps {
130114
query, ok := queryOverrides[name]
@@ -727,6 +711,12 @@ func NewExporter(dsn string, userQueriesPath string) *Exporter {
727711
}
728712
}
729713

714+
func (e *Exporter) Close() {
715+
if e.dbConnection != nil {
716+
e.dbConnection.Close() // nolint: errcheck
717+
}
718+
}
719+
730720
// Describe implements prometheus.Collector.
731721
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
732722
// We cannot know in advance what metrics the exporter will generate
@@ -969,12 +959,15 @@ func (e *Exporter) getDB(conn string) (*sql.DB, error) {
969959
if e.dbConnection == nil {
970960
d, err := sql.Open("postgres", conn)
971961
if err != nil {
962+
e.psqlUp.Set(0)
972963
return nil, err
973964
}
974965
err = d.Ping()
975966
if err != nil {
967+
e.psqlUp.Set(0)
976968
return nil, err
977969
}
970+
e.psqlUp.Set(1)
978971

979972
d.SetMaxOpenConns(1)
980973
d.SetMaxIdleConns(1)
@@ -1008,7 +1001,6 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
10081001
}
10091002
log.Infof("Error opening connection to database (%s): %s", loggableDsn, err)
10101003
e.error.Set(1)
1011-
e.psqlUp.Set(0) // Force "up" to 0 here.
10121004
return
10131005
}
10141006

@@ -1036,7 +1028,7 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
10361028
// DATA_SOURCE_NAME always wins so we do not break older versions
10371029
// reading secrets from files wins over secrets in environment variables
10381030
// DATA_SOURCE_NAME > DATA_SOURCE_{USER|FILE}_FILE > DATA_SOURCE_{USER|FILE}
1039-
func getDataSource() string {
1031+
func GetDataSource() string {
10401032
var dsn = os.Getenv("DATA_SOURCE_NAME")
10411033
if len(dsn) == 0 {
10421034
var user string
@@ -1068,48 +1060,3 @@ func getDataSource() string {
10681060

10691061
return dsn
10701062
}
1071-
1072-
func main() {
1073-
kingpin.Version(fmt.Sprintf("postgres_exporter %s (built with %s)\n", Version, runtime.Version()))
1074-
log.AddFlags(kingpin.CommandLine)
1075-
kingpin.Parse()
1076-
1077-
// landingPage contains the HTML served at '/'.
1078-
// TODO: Make this nicer and more informative.
1079-
var landingPage = []byte(`<html>
1080-
<head><title>Postgres exporter</title></head>
1081-
<body>
1082-
<h1>Postgres exporter</h1>
1083-
<p><a href='` + *metricPath + `'>Metrics</a></p>
1084-
</body>
1085-
</html>
1086-
`)
1087-
1088-
if *onlyDumpMaps {
1089-
dumpMaps()
1090-
return
1091-
}
1092-
1093-
dsn := getDataSource()
1094-
if len(dsn) == 0 {
1095-
log.Fatal("couldn't find environment variables describing the datasource to use")
1096-
}
1097-
1098-
exporter := NewExporter(dsn, *queriesPath)
1099-
defer func() {
1100-
if exporter.dbConnection != nil {
1101-
exporter.dbConnection.Close() // nolint: errcheck
1102-
}
1103-
}()
1104-
1105-
prometheus.MustRegister(exporter)
1106-
1107-
http.Handle(*metricPath, promhttp.Handler())
1108-
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
1109-
w.Header().Set("Content-Type", "Content-Type:text/plain; charset=UTF-8") // nolint: errcheck
1110-
w.Write(landingPage) // nolint: errcheck
1111-
})
1112-
1113-
log.Infof("Starting Server: %s", *listenAddress)
1114-
log.Fatal(http.ListenAndServe(*listenAddress, nil))
1115-
}

cmd/postgres_exporter/postgres_exporter_integration_test.go renamed to collector/postgres_exporter_integration_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,17 @@
33
// working.
44
// +build integration
55

6-
package main
6+
package collector
77

88
import (
9-
"os"
10-
"testing"
11-
12-
. "gopkg.in/check.v1"
13-
149
"database/sql"
1510
"fmt"
11+
"os"
12+
"testing"
1613

1714
_ "github.com/lib/pq"
1815
"github.com/prometheus/client_golang/prometheus"
16+
. "gopkg.in/check.v1"
1917
)
2018

2119
// Hook up gocheck into the "go test" runner.
@@ -78,6 +76,8 @@ func (s *IntegrationSuite) TestAllNamespacesReturnResults(c *C) {
7876
// the exporter. Related to https://github.com/wrouesnel/postgres_exporter/issues/93
7977
// although not a replication of the scenario.
8078
func (s *IntegrationSuite) TestInvalidDsnDoesntCrash(c *C) {
79+
queriesPath := os.Getenv("PG_EXPORTER_EXTEND_QUERY_PATH")
80+
8181
// Setup a dummy channel to consume metrics
8282
ch := make(chan prometheus.Metric, 100)
8383
go func() {
@@ -86,12 +86,12 @@ func (s *IntegrationSuite) TestInvalidDsnDoesntCrash(c *C) {
8686
}()
8787

8888
// Send a bad DSN
89-
exporter := NewExporter("invalid dsn", *queriesPath)
89+
exporter := NewExporter("invalid dsn", queriesPath)
9090
c.Assert(exporter, NotNil)
9191
exporter.scrape(ch)
9292

9393
// Send a DSN to a non-listening port.
94-
exporter = NewExporter("postgresql://nothing:[email protected]:1/nothing", *queriesPath)
94+
exporter = NewExporter("postgresql://nothing:[email protected]:1/nothing", queriesPath)
9595
c.Assert(exporter, NotNil)
9696
exporter.scrape(ch)
9797
}

cmd/postgres_exporter/postgres_exporter_test.go renamed to collector/postgres_exporter_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// +build !integration
22

3-
package main
3+
package collector
44

55
import (
66
. "gopkg.in/check.v1"
@@ -89,11 +89,11 @@ func (s *FunctionalSuite) TestSemanticVersionColumnDiscard(c *C) {
8989
// test read username and password from file
9090
func (s *FunctionalSuite) TestEnvironmentSettingWithSecretsFiles(c *C) {
9191

92-
err := os.Setenv("DATA_SOURCE_USER_FILE", "./tests/username_file")
92+
err := os.Setenv("DATA_SOURCE_USER_FILE", "../tests/username_file")
9393
c.Assert(err, IsNil)
9494
defer UnsetEnvironment(c, "DATA_SOURCE_USER_FILE")
9595

96-
err = os.Setenv("DATA_SOURCE_PASS_FILE", "./tests/userpass_file")
96+
err = os.Setenv("DATA_SOURCE_PASS_FILE", "../tests/userpass_file")
9797
c.Assert(err, IsNil)
9898
defer UnsetEnvironment(c, "DATA_SOURCE_PASS_FILE")
9999

@@ -103,7 +103,7 @@ func (s *FunctionalSuite) TestEnvironmentSettingWithSecretsFiles(c *C) {
103103

104104
var expected = "postgresql://custom_username:custom_password@localhost:5432/?sslmode=disable"
105105

106-
dsn := getDataSource()
106+
dsn := GetDataSource()
107107
if dsn != expected {
108108
c.Errorf("Expected Username to be read from file. Found=%v, expected=%v", dsn, expected)
109109
}
@@ -117,7 +117,7 @@ func (s *FunctionalSuite) TestEnvironmentSettingWithDns(c *C) {
117117
c.Assert(err, IsNil)
118118
defer UnsetEnvironment(c, "DATA_SOURCE_NAME")
119119

120-
dsn := getDataSource()
120+
dsn := GetDataSource()
121121
if dsn != envDsn {
122122
c.Errorf("Expected Username to be read from file. Found=%v, expected=%v", dsn, envDsn)
123123
}
@@ -131,15 +131,15 @@ func (s *FunctionalSuite) TestEnvironmentSettingWithDnsAndSecrets(c *C) {
131131
c.Assert(err, IsNil)
132132
defer UnsetEnvironment(c, "DATA_SOURCE_NAME")
133133

134-
err = os.Setenv("DATA_SOURCE_USER_FILE", "./tests/username_file")
134+
err = os.Setenv("DATA_SOURCE_USER_FILE", "../tests/username_file")
135135
c.Assert(err, IsNil)
136136
defer UnsetEnvironment(c, "DATA_SOURCE_USER_FILE")
137137

138138
err = os.Setenv("DATA_SOURCE_PASS", "envUserPass")
139139
c.Assert(err, IsNil)
140140
defer UnsetEnvironment(c, "DATA_SOURCE_PASS")
141141

142-
dsn := getDataSource()
142+
dsn := GetDataSource()
143143
if dsn != envDsn {
144144
c.Errorf("Expected Username to be read from file. Found=%v, expected=%v", dsn, envDsn)
145145
}

main.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"runtime"
7+
8+
_ "github.com/lib/pq"
9+
"github.com/prometheus/client_golang/prometheus"
10+
"github.com/prometheus/client_golang/prometheus/promhttp"
11+
"github.com/prometheus/common/log"
12+
"github.com/wrouesnel/postgres_exporter/collector"
13+
"gopkg.in/alecthomas/kingpin.v2"
14+
)
15+
16+
// Version is set during build to the git describe version
17+
// (semantic version)-(commitish) form.
18+
var Version = "0.0.1"
19+
20+
var (
21+
listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9187").OverrideDefaultFromEnvar("PG_EXPORTER_WEB_LISTEN_ADDRESS").String()
22+
metricPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").OverrideDefaultFromEnvar("PG_EXPORTER_WEB_TELEMETRY_PATH").String()
23+
queriesPath = kingpin.Flag("extend.query-path", "Path to custom queries to run.").Default("").OverrideDefaultFromEnvar("PG_EXPORTER_EXTEND_QUERY_PATH").String()
24+
onlyDumpMaps = kingpin.Flag("dumpmaps", "Do not run, simply dump the maps.").Bool()
25+
)
26+
27+
func main() {
28+
kingpin.Version(fmt.Sprintf("postgres_exporter %s (built with %s)\n", Version, runtime.Version()))
29+
log.AddFlags(kingpin.CommandLine)
30+
kingpin.Parse()
31+
32+
// landingPage contains the HTML served at '/'.
33+
// TODO: Make this nicer and more informative.
34+
var landingPage = []byte(`<html>
35+
<head><title>Postgres exporter</title></head>
36+
<body>
37+
<h1>Postgres exporter</h1>
38+
<p><a href='` + *metricPath + `'>Metrics</a></p>
39+
</body>
40+
</html>
41+
`)
42+
43+
if *onlyDumpMaps {
44+
collector.DumpMaps()
45+
return
46+
}
47+
48+
dsn := collector.GetDataSource()
49+
if len(dsn) == 0 {
50+
log.Fatal("couldn't find environment variables describing the datasource to use")
51+
}
52+
53+
exporter := collector.NewExporter(dsn, *queriesPath)
54+
defer exporter.Close()
55+
56+
prometheus.MustRegister(exporter)
57+
58+
http.Handle(*metricPath, promhttp.Handler())
59+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
60+
w.Header().Set("Content-Type", "Content-Type:text/plain; charset=UTF-8") // nolint: errcheck
61+
w.Write(landingPage) // nolint: errcheck
62+
})
63+
64+
log.Infof("Starting Server: %s", *listenAddress)
65+
log.Fatal(http.ListenAndServe(*listenAddress, nil))
66+
}

0 commit comments

Comments
 (0)