diff --git a/prometheus/push/push.go b/prometheus/push/push.go index 3de115d..02b11fa 100644 --- a/prometheus/push/push.go +++ b/prometheus/push/push.go @@ -117,14 +117,14 @@ func New(url, job string) *Pusher { // Push returns the first error encountered by any method call (including this // one) in the lifetime of the Pusher. func (p *Pusher) Push() error { - return p.push("PUT") + return p.push(http.MethodPut) } // Add works like push, but only previously pushed metrics with the same name // (and the same job and other grouping labels) will be replaced. (It uses HTTP // method “POST” to push to the Pushgateway.) func (p *Pusher) Add() error { - return p.push("POST") + return p.push(http.MethodPost) } // Gatherer adds a Gatherer to the Pusher, from which metrics will be gathered @@ -204,6 +204,42 @@ func (p *Pusher) Format(format expfmt.Format) *Pusher { return p } +// Delete sends a “DELETE” request to the Pushgateway configured while creating +// this Pusher, using the configured job name and any added grouping labels as +// grouping key. Any added Gatherers and Collectors added to this Pusher are +// ignored by this method. +// +// Delete returns the first error encountered by any method call (including this +// one) in the lifetime of the Pusher. +func (p *Pusher) Delete() error { + if p.error != nil { + return p.error + } + urlComponents := []string{url.QueryEscape(p.job)} + for ln, lv := range p.grouping { + urlComponents = append(urlComponents, ln, lv) + } + deleteURL := fmt.Sprintf("%s/metrics/job/%s", p.url, strings.Join(urlComponents, "/")) + + req, err := http.NewRequest(http.MethodDelete, deleteURL, nil) + if err != nil { + return err + } + if p.useBasicAuth { + req.SetBasicAuth(p.username, p.password) + } + resp, err := p.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != 202 { + body, _ := ioutil.ReadAll(resp.Body) // Ignore any further error as this is for an error message only. + return fmt.Errorf("unexpected status code %d while deleting %s: %s", resp.StatusCode, deleteURL, body) + } + return nil +} + func (p *Pusher) push(method string) error { if p.error != nil { return p.error diff --git a/prometheus/push/push_test.go b/prometheus/push/push_test.go index 5e19f39..0fc4110 100644 --- a/prometheus/push/push_test.go +++ b/prometheus/push/push_test.go @@ -93,8 +93,8 @@ func TestPush(t *testing.T) { Push(); err != nil { t.Fatal(err) } - if lastMethod != "PUT" { - t.Error("want method PUT for Push, got", lastMethod) + if lastMethod != http.MethodPut { + t.Errorf("got method %q for Push, want %q", lastMethod, http.MethodPut) } if !bytes.Equal(lastBody, wantBody) { t.Errorf("got body %v, want %v", lastBody, wantBody) @@ -110,8 +110,8 @@ func TestPush(t *testing.T) { Add(); err != nil { t.Fatal(err) } - if lastMethod != "POST" { - t.Error("want method POST for Add, got", lastMethod) + if lastMethod != http.MethodPost { + t.Errorf("got method %q for Add, want %q", lastMethod, http.MethodPost) } if !bytes.Equal(lastBody, wantBody) { t.Errorf("got body %v, want %v", lastBody, wantBody) @@ -167,8 +167,8 @@ func TestPush(t *testing.T) { Push(); err != nil { t.Fatal(err) } - if lastMethod != "PUT" { - t.Error("want method PUT for Push, got", lastMethod) + if lastMethod != http.MethodPut { + t.Errorf("got method %q for Push, want %q", lastMethod, http.MethodPut) } if !bytes.Equal(lastBody, wantBody) { t.Errorf("got body %v, want %v", lastBody, wantBody) @@ -182,8 +182,8 @@ func TestPush(t *testing.T) { Add(); err != nil { t.Fatal(err) } - if lastMethod != "POST" { - t.Error("want method POST for Add, got", lastMethod) + if lastMethod != http.MethodPost { + t.Errorf("got method %q for Add, want %q", lastMethod, http.MethodPost) } if !bytes.Equal(lastBody, wantBody) { t.Errorf("got body %v, want %v", lastBody, wantBody) @@ -191,4 +191,22 @@ func TestPush(t *testing.T) { if lastPath != "/metrics/job/testjob/a/x/b/y" && lastPath != "/metrics/job/testjob/b/y/a/x" { t.Error("unexpected path:", lastPath) } + + // Delete, all good. + if err := New(pgwOK.URL, "testjob"). + Grouping("a", "x"). + Grouping("b", "y"). + Delete(); err != nil { + t.Fatal(err) + } + if lastMethod != http.MethodDelete { + t.Errorf("got method %q for Delete, want %q", lastMethod, http.MethodDelete) + } + if len(lastBody) != 0 { + t.Errorf("got body of length %d, want empty body", len(lastBody)) + } + if lastPath != "/metrics/job/testjob/a/x/b/y" && lastPath != "/metrics/job/testjob/b/y/a/x" { + t.Error("unexpected path:", lastPath) + } + }