return error instead of panicing in InitPush* functions

This commit is contained in:
Aliaksandr Valialkin 2022-07-21 19:45:07 +03:00
parent c75f3497fe
commit f790ba580c
No known key found for this signature in database
GPG Key ID: A72BEC6CD3D0DED1
2 changed files with 43 additions and 26 deletions

36
push.go
View File

@ -7,6 +7,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
"net/url"
"time" "time"
) )
@ -23,11 +24,11 @@ import (
// //
// It is OK calling InitPushProcessMetrics multiple times with different pushURL - // It is OK calling InitPushProcessMetrics multiple times with different pushURL -
// in this case metrics are pushed to all the provided pushURL urls. // in this case metrics are pushed to all the provided pushURL urls.
func InitPushProcessMetrics(pushURL string, interval time.Duration, extraLabels string) { func InitPushProcessMetrics(pushURL string, interval time.Duration, extraLabels string) error {
writeMetrics := func(w io.Writer) { writeMetrics := func(w io.Writer) {
WriteProcessMetrics(w) WriteProcessMetrics(w)
} }
InitPushExt(pushURL, interval, extraLabels, writeMetrics) return InitPushExt(pushURL, interval, extraLabels, writeMetrics)
} }
// InitPush sets up periodic push for globally registered metrics to the given pushURL with the given interval. // InitPush sets up periodic push for globally registered metrics to the given pushURL with the given interval.
@ -45,11 +46,11 @@ func InitPushProcessMetrics(pushURL string, interval time.Duration, extraLabels
// //
// It is OK calling InitPush multiple times with different pushURL - // It is OK calling InitPush multiple times with different pushURL -
// in this case metrics are pushed to all the provided pushURL urls. // in this case metrics are pushed to all the provided pushURL urls.
func InitPush(pushURL string, interval time.Duration, extraLabels string, pushProcessMetrics bool) { func InitPush(pushURL string, interval time.Duration, extraLabels string, pushProcessMetrics bool) error {
writeMetrics := func(w io.Writer) { writeMetrics := func(w io.Writer) {
WritePrometheus(w, pushProcessMetrics) WritePrometheus(w, pushProcessMetrics)
} }
InitPushExt(pushURL, interval, extraLabels, writeMetrics) return InitPushExt(pushURL, interval, extraLabels, writeMetrics)
} }
// InitPush sets up periodic push for metrics from s to the given pushURL with the given interval. // InitPush sets up periodic push for metrics from s to the given pushURL with the given interval.
@ -65,11 +66,11 @@ func InitPush(pushURL string, interval time.Duration, extraLabels string, pushPr
// //
// It is OK calling InitPush multiple times with different pushURL - // It is OK calling InitPush multiple times with different pushURL -
// in this case metrics are pushed to all the provided pushURL urls. // in this case metrics are pushed to all the provided pushURL urls.
func (s *Set) InitPush(pushURL string, interval time.Duration, extraLabels string) { func (s *Set) InitPush(pushURL string, interval time.Duration, extraLabels string) error {
writeMetrics := func(w io.Writer) { writeMetrics := func(w io.Writer) {
s.WritePrometheus(w) s.WritePrometheus(w)
} }
InitPushExt(pushURL, interval, extraLabels, writeMetrics) return InitPushExt(pushURL, interval, extraLabels, writeMetrics)
} }
// InitPushExt sets up periodic push for metrics obtained by calling writeMetrics with the given interval. // InitPushExt sets up periodic push for metrics obtained by calling writeMetrics with the given interval.
@ -85,13 +86,24 @@ func (s *Set) InitPush(pushURL string, interval time.Duration, extraLabels strin
// //
// It is OK calling InitPushExt multiple times with different pushURL - // It is OK calling InitPushExt multiple times with different pushURL -
// in this case metrics are pushed to all the provided pushURL urls. // in this case metrics are pushed to all the provided pushURL urls.
func InitPushExt(pushURL string, interval time.Duration, extraLabels string, writeMetrics func(w io.Writer)) { func InitPushExt(pushURL string, interval time.Duration, extraLabels string, writeMetrics func(w io.Writer)) error {
if interval <= 0 { if interval <= 0 {
panic(fmt.Errorf("BUG: interval must be positive; got %s", interval)) return fmt.Errorf("interval must be positive; got %s", interval)
} }
if err := validateTags(extraLabels); err != nil { if err := validateTags(extraLabels); err != nil {
panic(fmt.Errorf("BUG: invalid extraLabels=%q: %s", extraLabels, err)) return fmt.Errorf("invalid extraLabels=%q: %w", extraLabels, err)
} }
pu, err := url.Parse(pushURL)
if err != nil {
return fmt.Errorf("cannot parse pushURL=%q: %w", pushURL, err)
}
if pu.Scheme != "http" && pu.Scheme != "https" {
return fmt.Errorf("unsupported scheme in pushURL=%q; expecting 'http' or 'https'", pushURL)
}
if pu.Host == "" {
return fmt.Errorf("missing host in pushURL=%q", pushURL)
}
pushURLRedacted := pu.Redacted()
c := &http.Client{ c := &http.Client{
Timeout: interval, Timeout: interval,
} }
@ -109,18 +121,20 @@ func InitPushExt(pushURL string, interval time.Duration, extraLabels string, wri
} }
resp, err := c.Post(pushURL, "text/plain", &bb) resp, err := c.Post(pushURL, "text/plain", &bb)
if err != nil { if err != nil {
log.Printf("ERROR: metrics.push: cannot push metrics to %q: %s", pushURL, err) log.Printf("ERROR: metrics.push: cannot push metrics to %q: %s", pushURLRedacted, err)
continue continue
} }
if resp.StatusCode/100 != 2 { if resp.StatusCode/100 != 2 {
body, _ := ioutil.ReadAll(resp.Body) body, _ := ioutil.ReadAll(resp.Body)
_ = resp.Body.Close() _ = resp.Body.Close()
log.Printf("ERROR: metrics.push: unexpected status code in response from %q: %d; expecting 2xx; response body: %q", pushURL, resp.StatusCode, body) log.Printf("ERROR: metrics.push: unexpected status code in response from %q: %d; expecting 2xx; response body: %q",
pushURLRedacted, resp.StatusCode, body)
continue continue
} }
_ = resp.Body.Close() _ = resp.Body.Close()
} }
}() }()
return nil
} }
func addExtraLabels(dst, src []byte, extraLabels string) []byte { func addExtraLabels(dst, src []byte, extraLabels string) []byte {

View File

@ -34,25 +34,28 @@ foobar{x="y",a="b",c="d"} 4
} }
func TestInitPushFailure(t *testing.T) { func TestInitPushFailure(t *testing.T) {
f := func(interval time.Duration, extraLabels string) { f := func(pushURL string, interval time.Duration, extraLabels string) {
t.Helper() t.Helper()
defer func() { if err := InitPush(pushURL, interval, extraLabels, false); err == nil {
if err := recover(); err == nil { t.Fatalf("expecting non-nil error")
panic("expecting non-nil error") }
}
}()
InitPush("http://foobar", interval, extraLabels, false)
} }
// Invalid url
f("foobar", time.Second, "")
f("aaa://foobar", time.Second, "")
f("http:///bar", time.Second, "")
// Non-positive interval // Non-positive interval
f(0, "") f("http://foobar", 0, "")
f("http://foobar", -time.Second, "")
// Invalid extraLabels // Invalid extraLabels
f(time.Second, "foo") f("http://foobar", time.Second, "foo")
f(time.Second, "foo{bar") f("http://foobar", time.Second, "foo{bar")
f(time.Second, "foo=bar") f("http://foobar", time.Second, "foo=bar")
f(time.Second, "foo='bar'") f("http://foobar", time.Second, "foo='bar'")
f(time.Second, `foo="bar",baz`) f("http://foobar", time.Second, `foo="bar",baz`)
f(time.Second, `{foo="bar"}`) f("http://foobar", time.Second, `{foo="bar"}`)
f(time.Second, `a{foo="bar"}`) f("http://foobar", time.Second, `a{foo="bar"}`)
} }