diff --git a/Makefile b/Makefile index 00b5488..cd3d46f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ OS_TYPE ?= $(shell uname -s | tr '[:upper:]' '[:lower:]') ARCH_TYPE ?= $(subst x86_64,amd64,$(patsubst i%86,386,$(ARCH))) GOOS ?= $(shell go env GOOS) GOARCH ?= $(shell go env GOARCH) -VERSION ?= 1.0.0 +VERSION ?= 1.1.0 LDFLAGS := -X main.Version=$(VERSION) GOFLAGS := -ldflags "$(LDFLAGS) -s -w" BUILD_ARGS = --build-arg VERSION=$(VERSION) diff --git a/README.md b/README.md index 7bd06ac..d9d97b7 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,14 @@ This project aims to provide observability for the Oracle Database so that users can understand performance and diagnose issues easily across applications and database. Over time, this project will provide not just metrics, but also logging and tracing support, and integration into popular frameworks like Spring Boot. The project aims to deliver functionality to support both cloud and on-premises databases, including those running in Kubernetes and containers. -In the first production release, v1.0, this project provides a [Prometheus](https://prometheus.io/) exporter for Oracle Database that is based in part on a Prometheus exporter created by [Seth Miller](https://github.com/iamseth/oracledb_exporter) with changes to comply with various Oracle standards and policies. +From the first production release, v1.0, onwards, this project provides a [Prometheus](https://prometheus.io/) exporter for Oracle Database that is based in part on a Prometheus exporter created by [Seth Miller](https://github.com/iamseth/oracledb_exporter) with changes to comply with various Oracle standards and policies. Contributions are welcome - please see [contributing](CONTRIBUTING.md). ### Table of Contents +- [Release Notes](#release-notes) - [Roadmap](#roadmap) - [Standard metrics](#standard-metrics) - [Database permissions required](#database-permissions-required) @@ -17,15 +18,26 @@ Contributions are welcome - please see [contributing](CONTRIBUTING.md). - [Test/demo environment using Docker Compose](#testdemo-environment-with-docker-compose) - [Kubernetes](#kubernetes) - [Standalone binary](#standalone-binary) + - [Using OCI Vault](#using-oci-vault) - [Custom metrics](#custom-metrics) - [Grafana dashboards](#grafana-dashboards) - [Monitoring Transactional Event Queues](#monitoring-transactional-event-queues) - [Developer notes](#developer-notes) +## Release Notes -## Roadmap +### Version 1.1, October 27, 2023 + +This release includes the following changes: -### Version 1.0 +- The query for the standard metric `wait_class` has been updated so that it will work in both container databases + and pluggable databases, including in Oracle Autonomous Database instances. Note that this query will not return + any data unless the database instance is under load. +- Support for reading the database password from OCI Vault has been added (see [details](#using-oci-vault)) +- Log messages have been improved +- Some dependencies have been updated + +### Version 1.0, September 13, 2023 The first production release, v1.0, includes the following features: @@ -41,7 +53,7 @@ Note that this exporter uses a different Oracle Database driver which in turn us The interfaces for this version have been kept as close as possible to those of earlier alpha releases in this repository to assist with migration. However, it should be expected that there may be breaking changes in future releases. -### Plans +## Roadmap We always welcome input on features you would like to see supported. Please open an issue in this repository with your suggestions. @@ -355,6 +367,14 @@ Usage of oracledb_exporter: Path to configuration file that can enable TLS or authentication. ``` +### Using OCI Vault + +The exporter will read the password from a secret stored in OCI Vault if you set these two environment +variables: + +- `VAULT_ID` should be set to the OCID of the OCI vault that you wish to use +- `VAULT_SECRET_NAME` should be set to the name of the secret in the OCI vault which contains the database password + ## Custom metrics The exporter allows definition of arbitrary custom metrics in a TOML file. To specify this file to the diff --git a/collector/collector.go b/collector/collector.go index fd530da..f1491e0 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -261,21 +261,22 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) { if err = e.db.Ping(); err != nil { if strings.Contains(err.Error(), "sql: database is closed") { - level.Info(e.logger).Log("Reconnecting to DB") + level.Info(e.logger).Log("msg", "Reconnecting to DB") err = e.connect() if err != nil { - level.Error(e.logger).Log("Error reconnecting to DB", err) + level.Error(e.logger).Log("msg", "Error reconnecting to DB", err) } } } if err = e.db.Ping(); err != nil { - level.Error(e.logger).Log("Error pinging oracle:", err) + level.Error(e.logger).Log("msg", "Error pinging oracle:", + "error", err) e.up.Set(0) return } - level.Debug(e.logger).Log("Successfully pinged Oracle database: ", maskDsn(e.connectString)) + level.Debug(e.logger).Log("msg", "Successfully pinged Oracle database: "+maskDsn(e.connectString)) e.up.Set(1) if e.checkIfMetricsChanged() { @@ -291,23 +292,23 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) { go func() { defer wg.Done() - level.Debug(e.logger).Log("About to scrape metric: ") - level.Debug(e.logger).Log("- Metric MetricsDesc: ", metric.MetricsDesc) - level.Debug(e.logger).Log("- Metric Context: ", metric.Context) - level.Debug(e.logger).Log("- Metric MetricsType: ", metric.MetricsType) - level.Debug(e.logger).Log("- Metric MetricsBuckets: ", metric.MetricsBuckets, "(Ignored unless Histogram type)") - level.Debug(e.logger).Log("- Metric Labels: ", metric.Labels) - level.Debug(e.logger).Log("- Metric FieldToAppend: ", metric.FieldToAppend) - level.Debug(e.logger).Log("- Metric IgnoreZeroResult: ", metric.IgnoreZeroResult) - level.Debug(e.logger).Log("- Metric Request: ", metric.Request) + level.Debug(e.logger).Log("msg", "About to scrape metric", + "Context", metric.Context, + "MetricsDesc", fmt.Sprint(metric.MetricsDesc), + "MetricsType", fmt.Sprint(metric.MetricsType), + "MetricsBuckets", fmt.Sprint(metric.MetricsBuckets), // ignored unless histogram + "Labels", fmt.Sprint(metric.Labels), + "FieldToAppend", metric.FieldToAppend, + "IgnoreZeroResult", metric.IgnoreZeroResult, + "Request", metric.Request) if len(metric.Request) == 0 { - level.Error(e.logger).Log("Error scraping for ", metric.MetricsDesc, ". Did you forget to define request in your toml file?") + level.Error(e.logger).Log("msg", "Error scraping for "+fmt.Sprint(metric.MetricsDesc)+". Did you forget to define request in your toml file?") return } if len(metric.MetricsDesc) == 0 { - level.Error(e.logger).Log("Error scraping for query", metric.Request, ". Did you forget to define metricsdesc in your toml file?") + level.Error(e.logger).Log("msg", "Error scraping for query"+fmt.Sprint(metric.Request)+". Did you forget to define metricsdesc in your toml file?") return } @@ -315,7 +316,7 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) { if metricType == "histogram" { _, ok := metric.MetricsBuckets[column] if !ok { - level.Error(e.logger).Log("Unable to find MetricsBuckets configuration key for metric. (metric=" + column + ")") + level.Error(e.logger).Log("msg", "Unable to find MetricsBuckets configuration key for metric. (metric="+column+")") return } } @@ -323,10 +324,17 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) { scrapeStart := time.Now() if err = e.ScrapeMetric(e.db, ch, metric); err != nil { - level.Error(e.logger).Log("Error scraping for", metric.Context, "_", metric.MetricsDesc, time.Since(scrapeStart), ":", err) + level.Error(e.logger).Log("msg", "Error scraping metric", + "Context", metric.Context, + "MetricsDesc", fmt.Sprint(metric.MetricsDesc), + "time", time.Since(scrapeStart), + "error", err) e.scrapeErrors.WithLabelValues(metric.Context).Inc() } else { - level.Debug(e.logger).Log("Successfully scraped metric: ", metric.Context, metric.MetricsDesc, time.Since(scrapeStart)) + level.Debug(e.logger).Log("msg", "Successfully scraped metric", + "Context", metric.Context, + "MetricDesc", fmt.Sprint(metric.MetricsDesc), + "time", time.Since(scrapeStart)) } }() } @@ -334,7 +342,7 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) { } func (e *Exporter) connect() error { - level.Debug(e.logger).Log("Launching connection: ", maskDsn(e.connectString)) + level.Debug(e.logger).Log("msg", "Launching connection to "+maskDsn(e.connectString)) var P godror.ConnectionParams P.Username, P.Password, P.ConnectString = e.user, godror.NewPassword(e.password), e.connectString @@ -344,11 +352,11 @@ func (e *Exporter) connect() error { // level.Error(e.logger).Log("Error while connecting to", e.dsn) // return err // } - level.Debug(e.logger).Log("set max idle connections to ", e.config.MaxIdleConns) + level.Debug(e.logger).Log("msg", "set max idle connections to "+strconv.Itoa(e.config.MaxIdleConns)) db.SetMaxIdleConns(e.config.MaxIdleConns) - level.Debug(e.logger).Log("set max open connections to ", e.config.MaxOpenConns) + level.Debug(e.logger).Log("msg", "set max open connections to "+strconv.Itoa(e.config.MaxOpenConns)) db.SetMaxOpenConns(e.config.MaxOpenConns) - level.Debug(e.logger).Log("Successfully connected to: ", maskDsn(e.connectString)) + level.Debug(e.logger).Log("msg", "Successfully connected to "+maskDsn(e.connectString)) e.db = db return nil } @@ -358,15 +366,15 @@ func (e *Exporter) checkIfMetricsChanged() bool { if len(_customMetrics) == 0 { continue } - level.Debug(e.logger).Log("Checking modifications in following metrics definition file:", _customMetrics) + level.Debug(e.logger).Log("msg", "Checking modifications in following metrics definition file:"+_customMetrics) h := sha256.New() if err := hashFile(h, _customMetrics); err != nil { - level.Error(e.logger).Log("Unable to get file hash", err) + level.Error(e.logger).Log("msg", "Unable to get file hash", "error", err) return false } // If any of files has been changed reload metrics if !bytes.Equal(hashMap[i], h.Sum(nil)) { - level.Info(e.logger).Log(_customMetrics, "has been changed. Reloading metrics...") + level.Info(e.logger).Log("msg", _customMetrics+" has been changed. Reloading metrics...") hashMap[i] = h.Sum(nil) return true } @@ -401,18 +409,18 @@ func (e *Exporter) reloadMetrics() { level.Error(e.logger).Log(err) panic(errors.New("Error while loading " + _customMetrics)) } else { - level.Info(e.logger).Log("Successfully loaded custom metrics from: " + _customMetrics) + level.Info(e.logger).Log("msg", "Successfully loaded custom metrics from "+_customMetrics) } e.metricsToScrape.Metric = append(e.metricsToScrape.Metric, additionalMetrics.Metric...) } } else { - level.Debug(e.logger).Log("No custom metrics defined.") + level.Debug(e.logger).Log("msg", "No custom metrics defined.") } } // ScrapeMetric is an interface method to call scrapeGenericValues using Metric struct values func (e *Exporter) ScrapeMetric(db *sql.DB, ch chan<- prometheus.Metric, metricDefinition Metric) error { - level.Debug(e.logger).Log("Calling function ScrapeGenericValues()") + level.Debug(e.logger).Log("msg", "Calling function ScrapeGenericValues()") return e.scrapeGenericValues(db, ch, metricDefinition.Context, metricDefinition.Labels, metricDefinition.MetricsDesc, metricDefinition.MetricsType, metricDefinition.MetricsBuckets, metricDefinition.FieldToAppend, metricDefinition.IgnoreZeroResult, @@ -434,11 +442,12 @@ func (e *Exporter) scrapeGenericValues(db *sql.DB, ch chan<- prometheus.Metric, value, err := strconv.ParseFloat(strings.TrimSpace(row[metric]), 64) // If not a float, skip current metric if err != nil { - level.Error(e.logger).Log("Unable to convert current value to float (metric=" + metric + - ",metricHelp=" + metricHelp + ",value=<" + row[metric] + ">)") + level.Error(e.logger).Log("msg", "Unable to convert current value to float (metric="+metric+ + ",metricHelp="+metricHelp+",value=<"+row[metric]+">)") continue } - level.Debug(e.logger).Log("Query result looks like: ", value) + level.Debug(e.logger).Log("msg", "Query result", + "value", value) // If metric do not use a field content in metric's name if strings.Compare(fieldToAppend, "") == 0 { desc := prometheus.NewDesc( @@ -449,21 +458,21 @@ func (e *Exporter) scrapeGenericValues(db *sql.DB, ch chan<- prometheus.Metric, if metricsType[strings.ToLower(metric)] == "histogram" { count, err := strconv.ParseUint(strings.TrimSpace(row["count"]), 10, 64) if err != nil { - level.Error(e.logger).Log("Unable to convert count value to int (metric=" + metric + - ",metricHelp=" + metricHelp + ",value=<" + row["count"] + ">)") + level.Error(e.logger).Log("msg", "Unable to convert count value to int (metric="+metric+ + ",metricHelp="+metricHelp+",value=<"+row["count"]+">)") continue } buckets := make(map[float64]uint64) for field, le := range metricsBuckets[metric] { lelimit, err := strconv.ParseFloat(strings.TrimSpace(le), 64) if err != nil { - level.Error(e.logger).Log("Unable to convert bucket limit value to float (metric=" + metric + - ",metricHelp=" + metricHelp + ",bucketlimit=<" + le + ">)") + level.Error(e.logger).Log("msg", "Unable to convert bucket limit value to float (metric="+metric+ + ",metricHelp="+metricHelp+",bucketlimit=<"+le+">)") continue } counter, err := strconv.ParseUint(strings.TrimSpace(row[field]), 10, 64) if err != nil { - level.Error(e.logger).Log("Unable to convert ", field, " value to int (metric="+metric+ + level.Error(e.logger).Log("msg", "Unable to convert ", field, " value to int (metric="+metric+ ",metricHelp="+metricHelp+",value=<"+row[field]+">)") continue } @@ -483,21 +492,21 @@ func (e *Exporter) scrapeGenericValues(db *sql.DB, ch chan<- prometheus.Metric, if metricsType[strings.ToLower(metric)] == "histogram" { count, err := strconv.ParseUint(strings.TrimSpace(row["count"]), 10, 64) if err != nil { - level.Error(e.logger).Log("Unable to convert count value to int (metric=" + metric + - ",metricHelp=" + metricHelp + ",value=<" + row["count"] + ">)") + level.Error(e.logger).Log("msg", "Unable to convert count value to int (metric="+metric+ + ",metricHelp="+metricHelp+",value=<"+row["count"]+">)") continue } buckets := make(map[float64]uint64) for field, le := range metricsBuckets[metric] { lelimit, err := strconv.ParseFloat(strings.TrimSpace(le), 64) if err != nil { - level.Error(e.logger).Log("Unable to convert bucket limit value to float (metric=" + metric + - ",metricHelp=" + metricHelp + ",bucketlimit=<" + le + ">)") + level.Error(e.logger).Log("msg", "Unable to convert bucket limit value to float (metric="+metric+ + ",metricHelp="+metricHelp+",bucketlimit=<"+le+">)") continue } counter, err := strconv.ParseUint(strings.TrimSpace(row[field]), 10, 64) if err != nil { - level.Error(e.logger).Log("Unable to convert ", field, " value to int (metric="+metric+ + level.Error(e.logger).Log("msg", "Unable to convert ", field, " value to int (metric="+metric+ ",metricHelp="+metricHelp+",value=<"+row[field]+">)") continue } @@ -512,14 +521,14 @@ func (e *Exporter) scrapeGenericValues(db *sql.DB, ch chan<- prometheus.Metric, } return nil } - level.Debug(e.logger).Log("Calling function GeneratePrometheusMetrics()") + level.Debug(e.logger).Log("msg", "Calling function GeneratePrometheusMetrics()") err := e.generatePrometheusMetrics(db, genericParser, request) - level.Debug(e.logger).Log("ScrapeGenericValues() - metricsCount: ", metricsCount) + level.Debug(e.logger).Log("msg", "ScrapeGenericValues() - metricsCount: "+strconv.Itoa(metricsCount)) if err != nil { return err } if !ignoreZeroResult && metricsCount == 0 { - return errors.New("No metrics found while parsing") + return errors.New("no metrics found while parsing, query returned no rows") } return err } diff --git a/collector/default_metrics.go b/collector/default_metrics.go index e93105c..ca00c58 100644 --- a/collector/default_metrics.go +++ b/collector/default_metrics.go @@ -50,13 +50,11 @@ context = "wait_time" metricsdesc = { value="Generic counter metric from v$waitclassmetric view in Oracle." } fieldtoappend= "wait_class" request = ''' -SELECT - n.wait_class as WAIT_CLASS, - round(m.time_waited/m.INTSIZE_CSEC,3) as VALUE -FROM - v$waitclassmetric m, v$system_wait_class n -WHERE - m.wait_class_id=n.wait_class_id AND n.wait_class != 'Idle' +SELECT wait_class as WAIT_CLASS, sum(time_waited) as VALUE +FROM gv$active_session_history +where wait_class is not null +and sample_time > sysdate - interval '1' hour +GROUP BY wait_class; ''' [[metric]] @@ -82,7 +80,8 @@ func (e *Exporter) DefaultMetrics() Metrics { var metricsToScrape Metrics if e.config.DefaultMetricsFile != "" { if _, err := toml.DecodeFile(filepath.Clean(e.config.DefaultMetricsFile), &metricsToScrape); err != nil { - level.Error(e.logger).Log(fmt.Sprintf("there was an issue while loading specified default metrics file at: "+e.config.DefaultMetricsFile+", proceeding to run with default metrics."), err) + level.Error(e.logger).Log("msg", fmt.Sprintf("there was an issue while loading specified default metrics file at: "+e.config.DefaultMetricsFile+", proceeding to run with default metrics."), + "error", err) } return metricsToScrape } diff --git a/default-metrics.toml b/default-metrics.toml index 0a5a3d1..3426c12 100644 --- a/default-metrics.toml +++ b/default-metrics.toml @@ -8,7 +8,10 @@ request = "SELECT status, type, COUNT(*) as value FROM v$session GROUP BY status context = "resource" labels = [ "resource_name" ] metricsdesc = { current_utilization= "Generic counter metric from v$resource_limit view in Oracle (current value).", limit_value="Generic counter metric from v$resource_limit view in Oracle (UNLIMITED: -1)." } -request="SELECT resource_name,current_utilization,CASE WHEN TRIM(limit_value) LIKE 'UNLIMITED' THEN '-1' ELSE TRIM(limit_value) END as limit_value FROM v$resource_limit" +request = ''' +SELECT resource_name, current_utilization, CASE WHEN TRIM(limit_value) LIKE 'UNLIMITED' THEN '-1' ELSE TRIM(limit_value) END as limit_value +FROM v$resource_limit +''' [[metric]] context = "asm_diskgroup" @@ -33,14 +36,13 @@ context = "wait_time" metricsdesc = { value="Generic counter metric from v$waitclassmetric view in Oracle." } fieldtoappend= "wait_class" request = ''' -SELECT - n.wait_class as WAIT_CLASS, - round(m.time_waited/m.INTSIZE_CSEC,3) as VALUE -FROM - v$waitclassmetric m, v$system_wait_class n -WHERE - m.wait_class_id=n.wait_class_id AND n.wait_class != 'Idle' +SELECT wait_class as WAIT_CLASS, sum(time_waited) as VALUE +FROM gv$active_session_history +where wait_class is not null +and sample_time > sysdate - interval '1' hour +GROUP BY wait_class ''' +ignorezeroresult = true [[metric]] context = "tablespace" diff --git a/docker-compose/compose.yaml b/docker-compose/compose.yaml index f515d51..ec2dbdd 100644 --- a/docker-compose/compose.yaml +++ b/docker-compose/compose.yaml @@ -43,7 +43,7 @@ services: start_period: 30s exporter: - image: container-registry.oracle.com/database/observability-exporter:1.0.0 + image: container-registry.oracle.com/database/observability-exporter:1.0.1 container_name: exporter ports: - 9161:9161 diff --git a/go.mod b/go.mod index 989da31..d398a86 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,13 @@ module github.com/oracle/oracle-db-appdev-monitoring go 1.19 require ( - github.com/BurntSushi/toml v1.2.1 + github.com/BurntSushi/toml v1.3.2 github.com/alecthomas/kingpin/v2 v2.3.2 github.com/go-kit/log v0.2.1 - github.com/prometheus/client_golang v1.15.1 - github.com/prometheus/common v0.44.0 + github.com/godror/godror v0.37.0 + github.com/oracle/oci-go-sdk/v65 v65.51.0 + github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/common v0.45.0 github.com/prometheus/exporter-toolkit v0.10.0 ) @@ -18,23 +20,26 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.2.3 // indirect - github.com/godror/godror v0.37.0 // indirect github.com/godror/knownpb v0.1.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect + github.com/prometheus/procfs v0.11.1 // indirect + github.com/sony/gobreaker v0.5.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect - golang.org/x/crypto v0.8.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.30.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 3f487b1..7419707 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU= github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= @@ -13,6 +15,7 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= @@ -24,6 +27,8 @@ github.com/godror/godror v0.37.0 h1:3wR3/1msywDE49PzuXh9UUiwWOBNri0RVQQcu3HU4UY= github.com/godror/godror v0.37.0/go.mod h1:jW1+pN+z/V0h28p9XZXVNtEvfZP/2EBfaSjKJLp3E4g= github.com/godror/knownpb v0.1.0 h1:dJPK8s/I3PQzGGaGcUStL2zIaaICNzKKAK8BzP1uLio= github.com/godror/knownpb v0.1.0/go.mod h1:4nRFbQo1dDuwKnblRXDxrfCFYeT4hjg3GjMqef58eRE= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -38,46 +43,80 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= +github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/oracle/oci-go-sdk/v65 v65.51.0 h1:BL1KjY4hTTvdmOuzYbULSwMwDSo2VApuaxLxfqtrkK0= +github.com/oracle/oci-go-sdk/v65 v65.51.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/exporter-toolkit v0.10.0 h1:yOAzZTi4M22ZzVxD+fhy1URTuNRj/36uQJJ5S8IPza8= github.com/prometheus/exporter-toolkit v0.10.0/go.mod h1:+sVFzuvV5JDyw+Ih6p3zFxZNVnKQa3x5qPmDSiPu4ZY= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/sijms/go-ora/v2 v2.7.6 h1:QyR1CKFxG+VVk2+LdHoHF4NxDSvcQ3deBXtZCrahSq4= github.com/sijms/go-ora/v2 v2.7.6/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= @@ -86,9 +125,13 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index dbedc53..b81c6db 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,7 @@ import ( // _ "net/http/pprof" "github.com/oracle/oracle-db-appdev-monitoring/collector" + "github.com/oracle/oracle-db-appdev-monitoring/vault" ) var ( @@ -50,6 +51,12 @@ func main() { password := os.Getenv("DB_PASSWORD") connectString := os.Getenv("DB_CONNECT_STRING") + vaultName, useVault := os.LookupEnv("VAULT_ID") + if useVault { + level.Info(logger).Log("msg", "VAULT_ID env var is present so using OCI Vault", "vault_name", vaultName) + password = vault.GetVaultSecret(vaultName, os.Getenv("VAULT_SECRET_NAME")) + } + config := &collector.Config{ User: user, Password: password, @@ -62,7 +69,7 @@ func main() { } exporter, err := collector.NewExporter(logger, config) if err != nil { - level.Error(logger).Log("unable to connect to DB", err) + level.Error(logger).Log("msg", "unable to connect to DB", "error", err) } if *scrapeInterval != 0 { @@ -88,7 +95,7 @@ func main() { server := &http.Server{} if err := web.ListenAndServe(server, toolkitFlags, logger); err != nil { - level.Error(logger).Log("msg", "Listening error", "reason", err) + level.Error(logger).Log("msg", "Listening error", "error", err) os.Exit(1) } } diff --git a/vault/vault.go b/vault/vault.go new file mode 100755 index 0000000..7338173 --- /dev/null +++ b/vault/vault.go @@ -0,0 +1,42 @@ +// Copyright (c) 2023, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.package vault + +package vault + +import ( + "context" + b64 "encoding/base64" + "strings" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/example/helpers" + "github.com/oracle/oci-go-sdk/v65/secrets" +) + +func GetVaultSecret(vaultId string, secretName string) string { + configProvider := common.ConfigurationProviderEnvironmentVariables("vault", "") + + client, err := secrets.NewSecretsClientWithConfigurationProvider(configProvider) + helpers.FatalIfError(err) + + req := secrets.GetSecretBundleByNameRequest{ + SecretName: common.String(secretName), + VaultId: common.String(vaultId)} + + resp, err := client.GetSecretBundleByName(context.Background(), req) + helpers.FatalIfError(err) + + rawSecret := getSecretFromBase64(resp) + return strings.TrimRight(rawSecret, "\r\n") // make sure a \r and/or \n didn't make it into the secret +} + +func getSecretFromBase64(resp secrets.GetSecretBundleByNameResponse) string { + base64Details, ok := resp.SecretBundleContent.(secrets.Base64SecretBundleContentDetails) + secret := "" + if ok { + secretBytes, _ := b64.StdEncoding.DecodeString(*base64Details.Content) + secret = string(secretBytes) + } + + return secret +}