Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,9 @@ databases:
username: ${DB_USERNAME}
## Database password
password: ${DB_PASSWORD}
## Database password file
## If specified, will load the database password from a file.
# passwordFile: ${DB_PASSWORD_FILE}
## Database connection url
url: localhost:1521/freepdb1

Expand Down Expand Up @@ -729,6 +732,11 @@ databases:
# label_name1: label_value1
# label_name2: label_value2

# Optionally configure prometheus webserver
#web:
# listenAddresses: [':9161']
# systemdSocket: true|false
# configFile: /path/to/webconfigfile

metrics:
## How often to scrape metrics. If not provided, metrics will be scraped on request.
Expand Down
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
Our current priorities are support for Exadata metrics. We expect to address these in an upcoming release.

This release includes the following changes:
- Enable configuration of the prometheus webserver from the config file using the `web` prefix.
- Allow loading of database password(s) from a file.
- Fixed a bug where database type (CDB, PDB, etc.) was not reported in certain situations.
- Fixed a bug where literal passwords containing the '$' character (in the config file) would be evaluated as environment variables. To use literal passwords with the '$' character, escape the '$' character with a second '$': `$test$pwd` becomes `$$test$$pwd`.

Expand Down
54 changes: 47 additions & 7 deletions collector/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
package collector

import (
"fmt"
"github.com/godror/godror/dsn"
"github.com/oracle/oracle-db-appdev-monitoring/azvault"
"github.com/oracle/oracle-db-appdev-monitoring/ocivault"
"github.com/prometheus/exporter-toolkit/web"
"gopkg.in/yaml.v2"
"log/slog"
"os"
Expand All @@ -15,15 +17,24 @@ import (
)

type MetricsConfiguration struct {
MetricsPath string `yaml:"metricsPath"`
Databases map[string]DatabaseConfig `yaml:"databases"`
Metrics MetricsFilesConfig `yaml:"metrics"`
Logging LoggingConfig `yaml:"log"`
ListenAddress string `yaml:"listenAddress"`
MetricsPath string `yaml:"metricsPath"`
Databases map[string]DatabaseConfig `yaml:"databases"`
Metrics MetricsFilesConfig `yaml:"metrics"`
Logging LoggingConfig `yaml:"log"`
Web WebConfig `yaml:"web"`
}

type WebConfig struct {
ListenAddresses *[]string `yaml:"listenAddresses"`
SystemdSocket *bool `yaml:"systemdSocket"`
ConfigFile *string `yaml:"configFile"`
}

type DatabaseConfig struct {
Username string
Password string
PasswordFile string `yaml:"passwordFile"`
URL string `yaml:"url"`
ConnectConfig `yaml:",inline"`
Vault *VaultConfig `yaml:"vault,omitempty"`
Expand Down Expand Up @@ -146,6 +157,14 @@ func (d DatabaseConfig) GetUsername() string {
}

func (d DatabaseConfig) GetPassword() string {
if d.PasswordFile != "" {
bytes, err := os.ReadFile(d.PasswordFile)
if err != nil {
// If there is an invalid file, exporter cannot continue processing.
panic(fmt.Errorf("failed to read password file: %v", err))
}
return string(bytes)
}
if d.isOCIVault() && d.Vault.OCI.PasswordSecret != "" {
return ocivault.GetVaultSecret(d.Vault.OCI.ID, d.Vault.OCI.PasswordSecret)
}
Expand All @@ -163,7 +182,7 @@ func (d DatabaseConfig) isAzureVault() bool {
return d.Vault != nil && d.Vault.Azure != nil
}

func LoadMetricsConfiguration(logger *slog.Logger, cfg *Config, path string) (*MetricsConfiguration, error) {
func LoadMetricsConfiguration(logger *slog.Logger, cfg *Config, path string, flags *web.FlagConfig) (*MetricsConfiguration, error) {
m := &MetricsConfiguration{}
if len(cfg.ConfigFile) > 0 {
content, err := os.ReadFile(cfg.ConfigFile)
Expand All @@ -186,21 +205,42 @@ func LoadMetricsConfiguration(logger *slog.Logger, cfg *Config, path string) (*M
m.Databases["default"] = m.defaultDatabase(cfg)
}

m.merge(cfg, path)
m.merge(cfg, path, flags)
return m, nil
}

func (m *MetricsConfiguration) merge(cfg *Config, path string) {
func (wc WebConfig) Flags() *web.FlagConfig {
return &web.FlagConfig{
WebListenAddresses: wc.ListenAddresses,
WebSystemdSocket: wc.SystemdSocket,
WebConfigFile: wc.ConfigFile,
}
}

func (m *MetricsConfiguration) merge(cfg *Config, path string, flags *web.FlagConfig) {
if len(m.MetricsPath) == 0 {
m.MetricsPath = path
}
m.mergeWebConfig(flags)
m.mergeLoggingConfig(cfg)
m.mergeMetricsConfig(cfg)
if m.Metrics.ScrapeInterval == nil {
m.Metrics.ScrapeInterval = &cfg.ScrapeInterval
}
}

func (m *MetricsConfiguration) mergeWebConfig(flags *web.FlagConfig) {
if m.Web.ListenAddresses == nil {
m.Web.ListenAddresses = flags.WebListenAddresses
}
if m.Web.SystemdSocket == nil {
m.Web.SystemdSocket = flags.WebSystemdSocket
}
if m.Web.ConfigFile == nil {
m.Web.ConfigFile = flags.WebConfigFile
}
}

func (m *MetricsConfiguration) mergeLoggingConfig(cfg *Config) {
if m.Logging.LogDisable == nil {
m.Logging.LogDisable = cfg.LoggingConfig.LogDisable
Expand Down
10 changes: 5 additions & 5 deletions collector/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ func (d *Database) constLabels(labels map[string]string) map[string]string {
func NewDatabase(logger *slog.Logger, dbname string, dbconfig DatabaseConfig) *Database {
db, dbtype := connect(logger, dbname, dbconfig)
return &Database{
Name: dbname,
Up: 0,
Session: db,
Type: dbtype,
Config: dbconfig,
Name: dbname,
Up: 0,
Session: db,
Type: dbtype,
Config: dbconfig,
}
}

Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ func main() {
LogDestination: *logDestination,
},
}
m, err := collector.LoadMetricsConfiguration(logger, config, *metricPath)
m, err := collector.LoadMetricsConfiguration(logger, config, *metricPath, toolkitFlags)
if err != nil {
logger.Error("unable to load metrics configuration", "error", err)
return
}

exporter := collector.NewExporter(logger, m)
if exporter.ScrapeInterval() != 0 {
ctx, cancel := context.WithCancel(context.Background())
Expand Down Expand Up @@ -194,7 +194,7 @@ func main() {

// start the main server thread
server := &http.Server{}
if err := web.ListenAndServe(server, toolkitFlags, logger); err != nil {
if err := web.ListenAndServe(server, m.Web.Flags(), logger); err != nil {
logger.Error("Listening error", "error", err)
os.Exit(1)
}
Expand Down