Repository: NetApp/harvest
Pull request #93 (2021-05-31 17:43:05)

Influx support url
to address #63, added optional parameter `url`: when it is used, `addr` will be discarded, no scheme and port will be added to construct the final URL.

also updated test and doc.


Patch:
diff --git a/cmd/exporters/influxdb/README.md b/cmd/exporters/influxdb/README.md
index 5fe42c76..04832e4a 100644
--- a/cmd/exporters/influxdb/README.md
+++ b/cmd/exporters/influxdb/README.md
@@ -9,11 +9,12 @@ The InfluxDB Exporter will format metrics into the InfluxDB's [line protocol](ht
 
 
 ## Parameters
-Overview of all parameters:
+Overview of all parameters is provided below. Notice that only one of `url` and `addr` should be provided (at least one is required).
 
 | parameter              | type         | description                                      | default                |
 |------------------------|--------------|--------------------------------------------------|------------------------|
-| `addr`                 | string       | address of the database            			   |		     			|
+| `url`                  | string       | URL of the database, format: `SCHEME://HOST[:PORT]`  |		  			|
+| `addr`                 | string       | address of the database, format: `[SCHEME://]HOST`   |		        	|
 | `port`                 | int, optional| port of the database                             | `8086`                 |
 | `bucket`               | string       | InfluxDB bucket to write                         |                        |
 | `org`                  | string       | InfluxDB organization name                       |                        |
diff --git a/cmd/exporters/influxdb/influxdb.go b/cmd/exporters/influxdb/influxdb.go
index 1db4a288..eb294881 100644
--- a/cmd/exporters/influxdb/influxdb.go
+++ b/cmd/exporters/influxdb/influxdb.go
@@ -13,6 +13,7 @@ import (
 	"io/ioutil"
 	"net/http"
 	"strconv"
+	"strings"
 	"time"
 )
 
@@ -50,19 +51,33 @@ func (e *InfluxDB) Init() error {
 		return err
 	}
 
-	var addr, port, bucket, org, v, p string
-	var err error
+	var (
+		url, addr, port, bucket, org, v, p string
+		err                                error
+	)
 
 	// check required / optional parameters
-	if addr = e.Params.GetChildContentS("addr"); addr == "" {
-		return errors.New(errors.MISSING_PARAM, "addr")
-	}
 
-	if port = e.Params.GetChildContentS("port"); port == "" {
-		e.Logger.Debug().Msgf("using default port [%s]", defaultPort)
-		port = defaultPort
-	} else if _, err = strconv.Atoi(port); err != nil {
-		return errors.New(errors.INVALID_PARAM, "port")
+	// user should provide either url or addr
+	// url is expected to be the full URL (optionallly with scheme)
+	// addr is expected to include host only (no scheme, no port)
+	if url = e.Params.GetChildContentS("url"); url == "" {
+		if addr = e.Params.GetChildContentS("addr"); addr == "" {
+			return errors.New(errors.MISSING_PARAM, "url or addr")
+		}
+
+		if port = e.Params.GetChildContentS("port"); port == "" {
+			e.Logger.Debug().Msgf("using default port [%s]", defaultPort)
+			port = defaultPort
+		} else if _, err = strconv.Atoi(port); err != nil {
+			return errors.New(errors.INVALID_PARAM, "port")
+		}
+
+		url = "http://" + addr + ":" + port
+	}
+	// make sure url ends with slash
+	if !strings.HasSuffix(url, "/") {
+		url += "/"
 	}
 
 	if bucket = e.Params.GetChildContentS("bucket"); bucket == "" {
@@ -104,7 +119,7 @@ func (e *InfluxDB) Init() error {
 	}
 
 	// construct client URL
-	e.url = fmt.Sprintf("http://%s:%s/api/v%s/write?org=%s&bucket=%s&precision=%s", addr, port, v, org, bucket, p)
+	e.url = fmt.Sprintf("%sapi/v%s/write?org=%s&bucket=%s&precision=%s", url, v, org, bucket, p)
 	e.Logger.Debug().Msgf("url= [%s]", e.url)
 
 	// construct HTTP client
diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go
index 6937535b..631d812c 100644
--- a/pkg/conf/conf.go
+++ b/pkg/conf/conf.go
@@ -266,6 +266,7 @@ type Exporter struct {
 	Port              *int      `yaml:"port,omitempty"`
 	Type              *string   `yaml:"exporter,omitempty"`
 	Addr              *string   `yaml:"addr,omitempty"`
+	Url               *string   `yaml:"url,omitempty"`
 	LocalHttpAddr     *string   `yaml:"local_http_addr,omitempty"`
 	GlobalPrefix      *string   `yaml:"global_prefix,omitempty"`
 	AllowedAddrs      *[]string `yaml:"allow_addrs,omitempty"`


Test patch:
diff --git a/cmd/exporters/influxdb/influxdb_test.go b/cmd/exporters/influxdb/influxdb_test.go
index f3360be7..3cdaf9c1 100644
--- a/cmd/exporters/influxdb/influxdb_test.go
+++ b/cmd/exporters/influxdb/influxdb_test.go
@@ -11,6 +11,59 @@ import (
 	"testing"
 )
 
+// test that the addr (and port) parameters
+// are handled properly to construct server URL
+func TestAddrParameter(t *testing.T) {
+
+	expectedURL := "http://localhost:8086/api/v2/write?org=netapp&bucket=harvest&precision=s"
+
+	opts := &options.Options{}
+	opts.Debug = true
+
+	params := node.NewS("")
+	params.NewChildS("addr", "localhost")
+	params.NewChildS("org", "netapp")
+	params.NewChildS("bucket", "harvest")
+	params.NewChildS("token", "xxxxxxx")
+
+	influx := &InfluxDB{AbstractExporter: exporter.New("InfluxDB", "influx-test", opts, params)}
+	if err := influx.Init(); err != nil {
+		t.Fatal(err)
+	}
+
+	if influx.url == expectedURL {
+		t.Logf("OK - url: [%s]", expectedURL)
+	} else {
+		t.Fatalf("FAIL - expected [%s]\n                             got [%s]", expectedURL, influx.url)
+	}
+}
+
+// test that the addr (and port) parameters
+// are handled properly to construct server URL
+func TestUrlParameter(t *testing.T) {
+
+	expectedURL := "https://some-valid-domain-name.net/api/v2/write?org=netapp&bucket=harvest&precision=s"
+
+	opts := &options.Options{}
+	opts.Debug = true
+
+	params := node.NewS("")
+	params.NewChildS("url", "https://some-valid-domain-name.net/")
+	params.NewChildS("org", "netapp")
+	params.NewChildS("bucket", "harvest")
+	params.NewChildS("token", "xxxxxxx")
+	influx := &InfluxDB{AbstractExporter: exporter.New("InfluxDB", "influx-test", opts, params)}
+	if err := influx.Init(); err != nil {
+		t.Fatal(err)
+	}
+
+	if influx.url == expectedURL {
+		t.Logf("OK - url: [%s]", expectedURL)
+	} else {
+		t.Fatalf("FAIL - expected [%s]\n       got [%s]", expectedURL, influx.url)
+	}
+}
+
 // test rendering in debug mode
 // this does not send to influxdb, but simply prints
 // rendered data
diff --git a/cmd/tools/doctor/doctor_test.go b/cmd/tools/doctor/doctor_test.go
index 755a9056..b0131ae4 100644
--- a/cmd/tools/doctor/doctor_test.go
+++ b/cmd/tools/doctor/doctor_test.go
@@ -46,12 +46,22 @@ func TestConfigToStruct(t *testing.T) {
 			*conf.Config.Defaults.Collectors)
 	}
 
-	allowedRegexes := (*conf.Config.Exporters)["influxy"].AllowedAddrsRegex
+	allowedRegexes := (*conf.Config.Exporters)["prometheus"].AllowedAddrsRegex
 	if (*allowedRegexes)[0] != "^192.168.0.\\d+$" {
 		t.Fatalf(`expected allow_addrs_regex to be ^192.168.0.\d+$ actual=%+v`,
 			(*allowedRegexes)[0])
 	}
 
+	influxyAddr := (*conf.Config.Exporters)["influxy"].Addr
+	if (*influxyAddr) != "localhost" {
+		t.Fatalf(`expected addr to be "localhost", actual=%+v`, (*influxyAddr))
+	}
+
+	influxyURL := (*conf.Config.Exporters)["influxz"].Url
+	if (*influxyURL) != "www.example.com/influxdb" {
+		t.Fatalf(`expected addr to be "www.example.com/influxdb", actual=%+v`, (*influxyURL))
+	}
+
 	collectors := (*conf.Config.Pollers)["infinity2"].Collectors
 	if (*collectors)[0] != "Zapi" {
 		t.Fatalf(`expected infinity2 collectors to contain Zapi actual=%+v`,
diff --git a/cmd/tools/doctor/testConfig.yml b/cmd/tools/doctor/testConfig.yml
index 8776b637..73d67e39 100644
--- a/cmd/tools/doctor/testConfig.yml
+++ b/cmd/tools/doctor/testConfig.yml
@@ -7,6 +7,8 @@ Exporters:
     exporter: Prometheus
     addr: 0.0.0.0
     port: 12990
+    allow_addrs_regex:
+      - ^192.168.0.\d+$
   pluto: # name of your exporter, can be any valid yaml string
     exporter: Prometheus
     port: 12990              # ignored since this exporter contains a consul block
@@ -22,9 +24,12 @@ Exporters:
     bucket: harvest
     org: harvest
     token: abcdefghijklmnopqrstuvwxyz
-    allow_addrs_regex:
-      - ^192.168.0.\d+$
-
+  influxz:
+    exporter: InfluxDB
+    url: www.example.com/influxdb
+    bucket: harvest
+    org: harvest
+    token: abcdefghijklmnopqrstuvwxyz
 Defaults:
   collectors:
     - Zapi