Skip to content

Commit 134ce60

Browse files
Setup exporter to conditionally compile for godror (#395)
* Setup exporter to conditionally compile for godror Signed-off-by: Anders Swanson <[email protected]>
1 parent 29573c0 commit 134ce60

File tree

6 files changed

+141
-132
lines changed

6 files changed

+141
-132
lines changed

collector/collector.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"sync"
2121
"time"
2222

23-
_ "github.com/godror/godror"
2423
"github.com/prometheus/client_golang/prometheus"
2524
)
2625

collector/config.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package collector
55

66
import (
77
"fmt"
8-
"github.com/godror/godror/dsn"
98
"github.com/oracle/oracle-db-appdev-monitoring/azvault"
109
"github.com/oracle/oracle-db-appdev-monitoring/ocivault"
1110
"github.com/prometheus/exporter-toolkit/web"
@@ -42,7 +41,7 @@ type DatabaseConfig struct {
4241
}
4342

4443
type ConnectConfig struct {
45-
Role dsn.AdminRole
44+
Role string
4645
TNSAdmin string `yaml:"tnsAdmin"`
4746
ExternalAuth bool `yaml:"externalAuth"`
4847
MaxOpenConns *int `yaml:"maxOpenConns"`

collector/connect_godror.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright (c) 2025, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
3+
4+
//go:build !goora
5+
6+
package collector
7+
8+
import (
9+
"context"
10+
"database/sql"
11+
"errors"
12+
"fmt"
13+
"github.com/godror/godror"
14+
"github.com/godror/godror/dsn"
15+
"log/slog"
16+
"strings"
17+
"time"
18+
)
19+
20+
func connect(logger *slog.Logger, dbname string, dbconfig DatabaseConfig) *sql.DB {
21+
logger.Debug("Launching connection to "+maskDsn(dbconfig.URL), "database", dbname)
22+
23+
var P godror.ConnectionParams
24+
password := dbconfig.GetPassword()
25+
username := dbconfig.GetUsername()
26+
// If password is not specified, externalAuth will be true, and we'll ignore user input
27+
dbconfig.ExternalAuth = password == ""
28+
logger.Debug(fmt.Sprintf("external authentication set to %t", dbconfig.ExternalAuth), "database", dbname)
29+
msg := "Using Username/Password Authentication."
30+
if dbconfig.ExternalAuth {
31+
msg = "Database Password not specified; will attempt to use external authentication (ignoring user input)."
32+
dbconfig.Username = ""
33+
}
34+
logger.Info(msg, "database", dbname)
35+
externalAuth := sql.NullBool{
36+
Bool: dbconfig.ExternalAuth,
37+
Valid: true,
38+
}
39+
P.Username, P.Password, P.ConnectString, P.ExternalAuth = username, godror.NewPassword(password), dbconfig.URL, externalAuth
40+
41+
if dbconfig.GetPoolIncrement() > 0 {
42+
logger.Debug(fmt.Sprintf("set pool increment to %d", dbconfig.PoolIncrement), "database", dbname)
43+
P.PoolParams.SessionIncrement = dbconfig.GetPoolIncrement()
44+
}
45+
if dbconfig.GetPoolMaxConnections() > 0 {
46+
logger.Debug(fmt.Sprintf("set pool max connections to %d", dbconfig.PoolMaxConnections), "database", dbname)
47+
P.PoolParams.MaxSessions = dbconfig.GetPoolMaxConnections()
48+
}
49+
if dbconfig.GetPoolMinConnections() > 0 {
50+
logger.Debug(fmt.Sprintf("set pool min connections to %d", dbconfig.PoolMinConnections), "database", dbname)
51+
P.PoolParams.MinSessions = dbconfig.GetPoolMinConnections()
52+
}
53+
54+
P.PoolParams.WaitTimeout = time.Second * 5
55+
56+
// if TNS_ADMIN env var is set, set ConfigDir to that location
57+
P.ConfigDir = dbconfig.TNSAdmin
58+
59+
switch dbconfig.Role {
60+
case "SYSDBA":
61+
P.AdminRole = dsn.SysDBA
62+
case "SYSOPER":
63+
P.AdminRole = dsn.SysOPER
64+
case "SYSBACKUP":
65+
P.AdminRole = dsn.SysBACKUP
66+
case "SYSDG":
67+
P.AdminRole = dsn.SysDG
68+
case "SYSKM":
69+
P.AdminRole = dsn.SysKM
70+
case "SYSRAC":
71+
P.AdminRole = dsn.SysRAC
72+
case "SYSASM":
73+
P.AdminRole = dsn.SysASM
74+
default:
75+
P.AdminRole = dsn.NoRole
76+
}
77+
78+
// note that this just configures the connection, it does not actually connect until later
79+
// when we call db.Ping()
80+
db := sql.OpenDB(godror.NewConnector(P))
81+
logger.Debug(fmt.Sprintf("set max idle connections to %d", dbconfig.MaxIdleConns), "database", dbname)
82+
db.SetMaxIdleConns(dbconfig.GetMaxIdleConns())
83+
logger.Debug(fmt.Sprintf("set max open connections to %d", dbconfig.MaxOpenConns), "database", dbname)
84+
db.SetMaxOpenConns(dbconfig.GetMaxOpenConns())
85+
db.SetConnMaxLifetime(0)
86+
logger.Debug(fmt.Sprintf("Successfully configured connection to %s", maskDsn(dbconfig.URL)), "database", dbname)
87+
88+
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
89+
defer cancel()
90+
if _, err := db.ExecContext(ctx, `
91+
begin
92+
dbms_application_info.set_client_info('oracledb_exporter');
93+
end;`); err != nil {
94+
logger.Info("Could not set CLIENT_INFO.", "database", dbname)
95+
}
96+
97+
var sysdba string
98+
if err := db.QueryRowContext(ctx, "select sys_context('USERENV', 'ISDBA') from dual").Scan(&sysdba); err != nil {
99+
logger.Error("error checking my database role", "error", err, "database", dbname)
100+
}
101+
logger.Info("Connected as SYSDBA? "+sysdba, "database", dbname)
102+
103+
return db
104+
}
105+
106+
// ping the database. If the database is disconnected, try to reconnect.
107+
// If the database type is unknown, try to reload it.
108+
func (d *Database) ping(logger *slog.Logger) error {
109+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
110+
defer cancel()
111+
err := d.Session.PingContext(ctx)
112+
if err != nil {
113+
d.Up = 0
114+
if isInvalidCredentialsError(err) {
115+
d.invalidate()
116+
return err
117+
}
118+
// If database is closed, try to reconnect
119+
if strings.Contains(err.Error(), "sql: database is closed") {
120+
d.Session = connect(logger, d.Name, d.Config)
121+
}
122+
return err
123+
}
124+
d.Up = 1
125+
return nil
126+
}
127+
128+
func isInvalidCredentialsError(err error) bool {
129+
err = errors.Unwrap(err)
130+
if err == nil {
131+
return false
132+
}
133+
oraErr, ok := err.(*godror.OraErr)
134+
if !ok {
135+
return false
136+
}
137+
return oraErr.Code() == ora01017code || oraErr.Code() == ora28000code
138+
}

collector/database.go

Lines changed: 0 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,8 @@ package collector
66
import (
77
"context"
88
"database/sql"
9-
"errors"
10-
"fmt"
11-
"github.com/godror/godror"
12-
"github.com/godror/godror/dsn"
139
"github.com/prometheus/client_golang/prometheus"
1410
"log/slog"
15-
"strings"
1611
"time"
1712
)
1813

@@ -34,28 +29,6 @@ func (d *Database) UpMetric(exporterLabels map[string]string) prometheus.Metric
3429
)
3530
}
3631

37-
// ping the database. If the database is disconnected, try to reconnect.
38-
// If the database type is unknown, try to reload it.
39-
func (d *Database) ping(logger *slog.Logger) error {
40-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
41-
defer cancel()
42-
err := d.Session.PingContext(ctx)
43-
if err != nil {
44-
d.Up = 0
45-
if isInvalidCredentialsError(err) {
46-
d.invalidate()
47-
return err
48-
}
49-
// If database is closed, try to reconnect
50-
if strings.Contains(err.Error(), "sql: database is closed") {
51-
d.Session = connect(logger, d.Name, d.Config)
52-
}
53-
return err
54-
}
55-
d.Up = 1
56-
return nil
57-
}
58-
5932
func (d *Database) constLabels(labels map[string]string) map[string]string {
6033
labels["database"] = d.Name
6134

@@ -125,101 +98,3 @@ func (d *Database) IsValid() bool {
12598
func (d *Database) invalidate() {
12699
d.Valid = false
127100
}
128-
129-
func isInvalidCredentialsError(err error) bool {
130-
err = errors.Unwrap(err)
131-
if err == nil {
132-
return false
133-
}
134-
oraErr, ok := err.(*godror.OraErr)
135-
if !ok {
136-
return false
137-
}
138-
return oraErr.Code() == ora01017code || oraErr.Code() == ora28000code
139-
}
140-
141-
func connect(logger *slog.Logger, dbname string, dbconfig DatabaseConfig) *sql.DB {
142-
logger.Debug("Launching connection to "+maskDsn(dbconfig.URL), "database", dbname)
143-
144-
var P godror.ConnectionParams
145-
password := dbconfig.GetPassword()
146-
username := dbconfig.GetUsername()
147-
// If password is not specified, externalAuth will be true, and we'll ignore user input
148-
dbconfig.ExternalAuth = password == ""
149-
logger.Debug(fmt.Sprintf("external authentication set to %t", dbconfig.ExternalAuth), "database", dbname)
150-
msg := "Using Username/Password Authentication."
151-
if dbconfig.ExternalAuth {
152-
msg = "Database Password not specified; will attempt to use external authentication (ignoring user input)."
153-
dbconfig.Username = ""
154-
}
155-
logger.Info(msg, "database", dbname)
156-
externalAuth := sql.NullBool{
157-
Bool: dbconfig.ExternalAuth,
158-
Valid: true,
159-
}
160-
P.Username, P.Password, P.ConnectString, P.ExternalAuth = username, godror.NewPassword(password), dbconfig.URL, externalAuth
161-
162-
if dbconfig.GetPoolIncrement() > 0 {
163-
logger.Debug(fmt.Sprintf("set pool increment to %d", dbconfig.PoolIncrement), "database", dbname)
164-
P.PoolParams.SessionIncrement = dbconfig.GetPoolIncrement()
165-
}
166-
if dbconfig.GetPoolMaxConnections() > 0 {
167-
logger.Debug(fmt.Sprintf("set pool max connections to %d", dbconfig.PoolMaxConnections), "database", dbname)
168-
P.PoolParams.MaxSessions = dbconfig.GetPoolMaxConnections()
169-
}
170-
if dbconfig.GetPoolMinConnections() > 0 {
171-
logger.Debug(fmt.Sprintf("set pool min connections to %d", dbconfig.PoolMinConnections), "database", dbname)
172-
P.PoolParams.MinSessions = dbconfig.GetPoolMinConnections()
173-
}
174-
175-
P.PoolParams.WaitTimeout = time.Second * 5
176-
177-
// if TNS_ADMIN env var is set, set ConfigDir to that location
178-
P.ConfigDir = dbconfig.TNSAdmin
179-
180-
switch dbconfig.Role {
181-
case "SYSDBA":
182-
P.AdminRole = dsn.SysDBA
183-
case "SYSOPER":
184-
P.AdminRole = dsn.SysOPER
185-
case "SYSBACKUP":
186-
P.AdminRole = dsn.SysBACKUP
187-
case "SYSDG":
188-
P.AdminRole = dsn.SysDG
189-
case "SYSKM":
190-
P.AdminRole = dsn.SysKM
191-
case "SYSRAC":
192-
P.AdminRole = dsn.SysRAC
193-
case "SYSASM":
194-
P.AdminRole = dsn.SysASM
195-
default:
196-
P.AdminRole = dsn.NoRole
197-
}
198-
199-
// note that this just configures the connection, it does not actually connect until later
200-
// when we call db.Ping()
201-
db := sql.OpenDB(godror.NewConnector(P))
202-
logger.Debug(fmt.Sprintf("set max idle connections to %d", dbconfig.MaxIdleConns), "database", dbname)
203-
db.SetMaxIdleConns(dbconfig.GetMaxIdleConns())
204-
logger.Debug(fmt.Sprintf("set max open connections to %d", dbconfig.MaxOpenConns), "database", dbname)
205-
db.SetMaxOpenConns(dbconfig.GetMaxOpenConns())
206-
db.SetConnMaxLifetime(0)
207-
logger.Debug(fmt.Sprintf("Successfully configured connection to %s", maskDsn(dbconfig.URL)), "database", dbname)
208-
209-
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
210-
defer cancel()
211-
if _, err := db.ExecContext(ctx, `
212-
begin
213-
dbms_application_info.set_client_info('oracledb_exporter');
214-
end;`); err != nil {
215-
logger.Info("Could not set CLIENT_INFO.", "database", dbname)
216-
}
217-
218-
var sysdba string
219-
if err := db.QueryRowContext(ctx, "select sys_context('USERENV', 'ISDBA') from dual").Scan(&sysdba); err != nil {
220-
logger.Error("error checking my database role", "error", err, "database", dbname)
221-
}
222-
logger.Info("Connected as SYSDBA? "+sysdba, "database", dbname)
223-
224-
return db
225-
}

collector/types.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ package collector
44

55
import (
66
"database/sql"
7-
"github.com/godror/godror/dsn"
87
"github.com/prometheus/client_golang/prometheus"
98
"log/slog"
109
"sync"
@@ -59,7 +58,7 @@ type Config struct {
5958
User string
6059
Password string
6160
ConnectString string
62-
DbRole dsn.AdminRole
61+
DbRole string
6362
ConfigDir string
6463
ExternalAuth bool
6564
MaxIdleConns int

main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"github.com/prometheus/common/promslog"
1717
"github.com/prometheus/common/promslog/flag"
1818

19-
"github.com/godror/godror/dsn"
2019
"github.com/prometheus/client_golang/prometheus"
2120
cversion "github.com/prometheus/client_golang/prometheus/collectors/version"
2221
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -87,7 +86,7 @@ func main() {
8786
User: user,
8887
Password: password,
8988
ConnectString: connectString,
90-
DbRole: dsn.AdminRole(dbrole),
89+
DbRole: dbrole,
9190
ConfigDir: tnsadmin,
9291
ExternalAuth: externalAuth,
9392
MaxOpenConns: *maxOpenConns,

0 commit comments

Comments
 (0)