@@ -950,8 +950,9 @@ func NewServers(opts ...ServerOpt) *Servers {
950
950
}
951
951
}
952
952
953
- // GetServer returns established connection from a collection.
954
- func (s * Servers ) GetServer (dsn string ) (* Server , error ) {
953
+ // GetServer returns established connection from a collection. If a connection
954
+ // is invalid a new connection is established.
955
+ func (s * Servers ) GetServer (dsnID string , dsn string ) (* Server , error ) {
955
956
s .m .Lock ()
956
957
defer s .m .Unlock ()
957
958
var err error
@@ -963,17 +964,17 @@ func (s *Servers) GetServer(dsn string) (*Server, error) {
963
964
if errCount ++ ; errCount > retries {
964
965
return nil , err
965
966
}
966
- server , ok = s .servers [dsn ]
967
+ server , ok = s .servers [dsnID ]
967
968
if ! ok {
968
969
server , err = NewServer (dsn , s .opts ... )
969
970
if err != nil {
970
971
time .Sleep (time .Duration (errCount ) * time .Second )
971
972
continue
972
973
}
973
- s .servers [dsn ] = server
974
+ s .servers [dsnID ] = server
974
975
}
975
976
if err = server .Ping (); err != nil {
976
- delete (s .servers , dsn )
977
+ delete (s .servers , dsnID )
977
978
time .Sleep (time .Duration (errCount ) * time .Second )
978
979
continue
979
980
}
@@ -1002,7 +1003,7 @@ type Exporter struct {
1002
1003
disableDefaultMetrics , disableSettingsMetrics , autoDiscoverDatabases bool
1003
1004
1004
1005
excludeDatabases []string
1005
- dsn [] string
1006
+ dsnFactories [] DsnFactory
1006
1007
userQueriesPath string
1007
1008
constantLabels prometheus.Labels
1008
1009
duration prometheus.Gauge
@@ -1088,9 +1089,9 @@ func parseConstLabels(s string) prometheus.Labels {
1088
1089
}
1089
1090
1090
1091
// NewExporter returns a new PostgreSQL exporter for the provided DSN.
1091
- func NewExporter (dsn []string , opts ... ExporterOpt ) * Exporter {
1092
+ func NewExporter (dsnFactories []DsnFactory , opts ... ExporterOpt ) * Exporter {
1092
1093
e := & Exporter {
1093
- dsn : dsn ,
1094
+ dsnFactories : dsnFactories ,
1094
1095
builtinMetricMaps : builtinMetricMaps ,
1095
1096
}
1096
1097
@@ -1459,18 +1460,33 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
1459
1460
1460
1461
e .totalScrapes .Inc ()
1461
1462
1462
- dsns := e .dsn
1463
+ var errorsCount int
1464
+ var dsns map [string ]string
1465
+
1466
+ dsnFactories := e .dsnFactories
1467
+
1463
1468
if e .autoDiscoverDatabases {
1464
1469
dsns = e .discoverDatabaseDSNs ()
1470
+ } else {
1471
+ dsns = make (map [string ]string )
1472
+ for _ , dsnFactory := range dsnFactories {
1473
+ dsn , err := dsnFactory .NewDsn ()
1474
+
1475
+ if err != nil {
1476
+ log .Errorf ("Unable to create dsn (%s): %v" , loggableDSN (dsnFactory .DsnID ()), err )
1477
+
1478
+ errorsCount ++
1479
+ } else {
1480
+ dsns [dsnFactory .DsnID ()] = dsn
1481
+ }
1482
+ }
1465
1483
}
1466
1484
1467
- var errorsCount int
1468
1485
var connectionErrorsCount int
1469
1486
1470
- for _ , dsn := range dsns {
1471
- if err := e .scrapeDSN (ch , dsn ); err != nil {
1487
+ for dsnID , dsn := range dsns {
1488
+ if err := e .scrapeDSN (ch , dsnID , dsn ); err != nil {
1472
1489
errorsCount ++
1473
-
1474
1490
log .Errorf (err .Error ())
1475
1491
1476
1492
if _ , ok := err .(* ErrorConnectToServer ); ok {
@@ -1494,17 +1510,31 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) {
1494
1510
}
1495
1511
}
1496
1512
1497
- func (e * Exporter ) discoverDatabaseDSNs () []string {
1498
- dsns := make (map [string ]struct {})
1499
- for _ , dsn := range e .dsn {
1500
- parsedDSN , err := url .Parse (dsn )
1513
+ func (e * Exporter ) discoverDatabaseDSNs () map [string ]string {
1514
+ dsns := make (map [string ]string )
1515
+ for _ , dsnFactory := range e .dsnFactories {
1516
+ dsnID := dsnFactory .DsnID ()
1517
+ dsn , err := dsnFactory .NewDsn ()
1518
+ if err != nil {
1519
+ log .Errorf ("Unable to create DSN (%s): %v" , loggableDSN (dsnFactory .DsnID ()), err )
1520
+ }
1521
+
1522
+ // Validate the DSN returned from the factory
1523
+ parsedDsn , err := url .Parse (dsn )
1501
1524
if err != nil {
1502
1525
log .Errorf ("Unable to parse DSN (%s): %v" , loggableDSN (dsn ), err )
1503
1526
continue
1504
1527
}
1505
1528
1506
- dsns [dsn ] = struct {}{}
1507
- server , err := e .servers .GetServer (dsn )
1529
+ // Validate and DSN Id assigned to the factory
1530
+ parsedDsnID , err := url .Parse (dsnID )
1531
+ if err != nil {
1532
+ log .Errorf ("Unable to parse DSN Id (%s): %v" , loggableDSN (dsnID ), err )
1533
+ continue
1534
+ }
1535
+
1536
+ dsns [dsnID ] = dsn
1537
+ server , err := e .servers .GetServer (dsnID , dsn )
1508
1538
if err != nil {
1509
1539
log .Errorf ("Error opening connection to database (%s): %v" , loggableDSN (dsn ), err )
1510
1540
continue
@@ -1522,23 +1552,19 @@ func (e *Exporter) discoverDatabaseDSNs() []string {
1522
1552
if contains (e .excludeDatabases , databaseName ) {
1523
1553
continue
1524
1554
}
1525
- parsedDSN .Path = databaseName
1526
- dsns [parsedDSN .String ()] = struct {}{}
1527
- }
1528
- }
1529
1555
1530
- result := make ([] string , len ( dsns ))
1531
- index := 0
1532
- for dsn := range dsns {
1533
- result [ index ] = dsn
1534
- index ++
1556
+ parsedDsn . Path = databaseName
1557
+ parsedDsnID . Path = databaseName
1558
+
1559
+ dsns [ parsedDsnID . String () ] = parsedDsn . String ()
1560
+ }
1535
1561
}
1536
1562
1537
- return result
1563
+ return dsns
1538
1564
}
1539
1565
1540
- func (e * Exporter ) scrapeDSN (ch chan <- prometheus.Metric , dsn string ) error {
1541
- server , err := e .servers .GetServer (dsn )
1566
+ func (e * Exporter ) scrapeDSN (ch chan <- prometheus.Metric , dsnID string , dsn string ) error {
1567
+ server , err := e .servers .GetServer (dsnID , dsn )
1542
1568
1543
1569
if err != nil {
1544
1570
return & ErrorConnectToServer {fmt .Sprintf ("Error opening connection to database (%s): %s" , loggableDSN (dsn ), err .Error ())}
@@ -1561,9 +1587,12 @@ func (e *Exporter) scrapeDSN(ch chan<- prometheus.Metric, dsn string) error {
1561
1587
// DATA_SOURCE_NAME always wins so we do not break older versions
1562
1588
// reading secrets from files wins over secrets in environment variables
1563
1589
// DATA_SOURCE_NAME > DATA_SOURCE_{USER|PASS}_FILE > DATA_SOURCE_{USER|PASS}
1564
- func getDataSources () []string {
1590
+ func getDataSources () []DsnFactory {
1565
1591
var dsn = os .Getenv ("DATA_SOURCE_NAME" )
1566
- if len (dsn ) == 0 {
1592
+ var dsns []string
1593
+ if len (dsn ) > 0 {
1594
+ dsns = strings .Split (dsn , "," )
1595
+ } else {
1567
1596
var user string
1568
1597
var pass string
1569
1598
var uri string
@@ -1602,9 +1631,23 @@ func getDataSources() []string {
1602
1631
1603
1632
dsn = "postgresql://" + ui + "@" + uri
1604
1633
1605
- return []string {dsn }
1634
+ dsns = []string {dsn }
1635
+ }
1636
+
1637
+ dsnFactories := make ([]DsnFactory , 0 , len (dsns ))
1638
+ for _ , dsn := range dsns {
1639
+ u , _ := url .Parse (dsn )
1640
+
1641
+ if pw , set := u .User .Password (); set && pw == "AWS_RDS_IAM_AUTH" {
1642
+ log .Infof ("Building AwsRdsIamFactory for %s" , loggableDSN (dsn ))
1643
+ dsnFactories = append (dsnFactories , & AwsRdsIamFactory {dsn : dsn })
1644
+ } else {
1645
+ log .Infof ("Building RawDsnFactory for %s" , loggableDSN (dsn ))
1646
+ dsnFactories = append (dsnFactories , & RawDsnFactory {dsn : dsn })
1647
+ }
1606
1648
}
1607
- return strings .Split (dsn , "," )
1649
+
1650
+ return dsnFactories
1608
1651
}
1609
1652
1610
1653
func contains (a []string , x string ) bool {
@@ -1637,12 +1680,12 @@ func main() {
1637
1680
return
1638
1681
}
1639
1682
1640
- dsn := getDataSources ()
1641
- if len (dsn ) == 0 {
1683
+ dsnFactories := getDataSources ()
1684
+ if len (dsnFactories ) == 0 {
1642
1685
log .Fatal ("couldn't find environment variables describing the datasource to use" )
1643
1686
}
1644
1687
1645
- exporter := NewExporter (dsn ,
1688
+ exporter := NewExporter (dsnFactories ,
1646
1689
DisableDefaultMetrics (* disableDefaultMetrics ),
1647
1690
DisableSettingsMetrics (* disableSettingsMetrics ),
1648
1691
AutoDiscoverDatabases (* autoDiscoverDatabases ),
0 commit comments