@@ -37,6 +37,7 @@ package push
37
37
import (
38
38
"bytes"
39
39
"encoding/base64"
40
+ "errors"
40
41
"fmt"
41
42
"io/ioutil"
42
43
"net/http"
@@ -56,6 +57,8 @@ const (
56
57
base64Suffix = "@base64"
57
58
)
58
59
60
+ var errJobEmpty = errors .New ("job name is empty" )
61
+
59
62
// HTTPDoer is an interface for the one method of http.Client that is used by Pusher
60
63
type HTTPDoer interface {
61
64
Do (* http.Request ) (* http.Response , error )
@@ -80,14 +83,17 @@ type Pusher struct {
80
83
}
81
84
82
85
// New creates a new Pusher to push to the provided URL with the provided job
83
- // name. You can use just host:port or ip:port as url, in which case “http://”
84
- // is added automatically. Alternatively, include the schema in the
85
- // URL. However, do not include the “/metrics/jobs/…” part.
86
+ // name (which must not be empty) . You can use just host:port or ip:port as url,
87
+ // in which case “http://” is added automatically. Alternatively, include the
88
+ // schema in the URL. However, do not include the “/metrics/jobs/…” part.
86
89
func New (url , job string ) * Pusher {
87
90
var (
88
91
reg = prometheus .NewRegistry ()
89
92
err error
90
93
)
94
+ if job == "" {
95
+ err = errJobEmpty
96
+ }
91
97
if ! strings .Contains (url , "://" ) {
92
98
url = "http://" + url
93
99
}
@@ -267,7 +273,7 @@ func (p *Pusher) push(method string) error {
267
273
return err
268
274
}
269
275
defer resp .Body .Close ()
270
- // Pushgateway 0.10+ responds with StatusOK, earlier versions with StatusAccepted.
276
+ // Depending on version and configuration of the PGW, StatusOK or StatusAccepted may be returned .
271
277
if resp .StatusCode != http .StatusOK && resp .StatusCode != http .StatusAccepted {
272
278
body , _ := ioutil .ReadAll (resp .Body ) // Ignore any further error as this is for an error message only.
273
279
return fmt .Errorf ("unexpected status code %d while pushing to %s: %s" , resp .StatusCode , p .fullURL (), body )
@@ -278,9 +284,11 @@ func (p *Pusher) push(method string) error {
278
284
// fullURL assembles the URL used to push/delete metrics and returns it as a
279
285
// string. The job name and any grouping label values containing a '/' will
280
286
// trigger a base64 encoding of the affected component and proper suffixing of
281
- // the preceding component. If the component does not contain a '/' but other
282
- // special character, the usual url.QueryEscape is used for compatibility with
283
- // older versions of the Pushgateway and for better readability.
287
+ // the preceding component. Similarly, an empty grouping label value will be
288
+ // encoded as base64 just with a single `=` padding character (to avoid an empty
289
+ // path component). If the component does not contain a '/' but other special
290
+ // characters, the usual url.QueryEscape is used for compatibility with older
291
+ // versions of the Pushgateway and for better readability.
284
292
func (p * Pusher ) fullURL () string {
285
293
urlComponents := []string {}
286
294
if encodedJob , base64 := encodeComponent (p .job ); base64 {
@@ -299,9 +307,12 @@ func (p *Pusher) fullURL() string {
299
307
}
300
308
301
309
// encodeComponent encodes the provided string with base64.RawURLEncoding in
302
- // case it contains '/'. If not, it uses url.QueryEscape instead. It returns
303
- // true in the former case .
310
+ // case it contains '/' and as "=" in case it is empty. If neither is the case,
311
+ // it uses url.QueryEscape instead. It returns true in the former two cases .
304
312
func encodeComponent (s string ) (string , bool ) {
313
+ if s == "" {
314
+ return "=" , true
315
+ }
305
316
if strings .Contains (s , "/" ) {
306
317
return base64 .RawURLEncoding .EncodeToString ([]byte (s )), true
307
318
}
0 commit comments