1// Copyright 2016 The Prometheus Authors 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14// Copyright (c) 2013, The Prometheus Authors 15// All rights reserved. 16// 17// Use of this source code is governed by a BSD-style license that can be found 18// in the LICENSE file. 19 20package push 21 22import ( 23 "bytes" 24 "io/ioutil" 25 "net/http" 26 "net/http/httptest" 27 "os" 28 "testing" 29 30 "github.com/prometheus/common/expfmt" 31 32 "github.com/prometheus/client_golang/prometheus" 33) 34 35func TestPush(t *testing.T) { 36 37 var ( 38 lastMethod string 39 lastBody []byte 40 lastPath string 41 ) 42 43 host, err := os.Hostname() 44 if err != nil { 45 t.Error(err) 46 } 47 48 // Fake a Pushgateway that always responds with 202. 49 pgwOK := httptest.NewServer( 50 http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 51 lastMethod = r.Method 52 var err error 53 lastBody, err = ioutil.ReadAll(r.Body) 54 if err != nil { 55 t.Fatal(err) 56 } 57 lastPath = r.URL.EscapedPath() 58 w.Header().Set("Content-Type", `text/plain; charset=utf-8`) 59 w.WriteHeader(http.StatusAccepted) 60 }), 61 ) 62 defer pgwOK.Close() 63 64 // Fake a Pushgateway that always responds with 500. 65 pgwErr := httptest.NewServer( 66 http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 67 http.Error(w, "fake error", http.StatusInternalServerError) 68 }), 69 ) 70 defer pgwErr.Close() 71 72 metric1 := prometheus.NewCounter(prometheus.CounterOpts{ 73 Name: "testname1", 74 Help: "testhelp1", 75 }) 76 metric2 := prometheus.NewGauge(prometheus.GaugeOpts{ 77 Name: "testname2", 78 Help: "testhelp2", 79 ConstLabels: prometheus.Labels{"foo": "bar", "dings": "bums"}, 80 }) 81 82 reg := prometheus.NewRegistry() 83 reg.MustRegister(metric1) 84 reg.MustRegister(metric2) 85 86 mfs, err := reg.Gather() 87 if err != nil { 88 t.Fatal(err) 89 } 90 91 buf := &bytes.Buffer{} 92 enc := expfmt.NewEncoder(buf, expfmt.FmtProtoDelim) 93 94 for _, mf := range mfs { 95 if err := enc.Encode(mf); err != nil { 96 t.Fatal(err) 97 } 98 } 99 wantBody := buf.Bytes() 100 101 // PushCollectors, all good. 102 if err := Collectors("testjob", HostnameGroupingKey(), pgwOK.URL, metric1, metric2); err != nil { 103 t.Fatal(err) 104 } 105 if lastMethod != "PUT" { 106 t.Error("want method PUT for PushCollectors, got", lastMethod) 107 } 108 if bytes.Compare(lastBody, wantBody) != 0 { 109 t.Errorf("got body %v, want %v", lastBody, wantBody) 110 } 111 if lastPath != "/metrics/job/testjob/instance/"+host { 112 t.Error("unexpected path:", lastPath) 113 } 114 115 // PushAddCollectors, with nil grouping, all good. 116 if err := AddCollectors("testjob", nil, pgwOK.URL, metric1, metric2); err != nil { 117 t.Fatal(err) 118 } 119 if lastMethod != "POST" { 120 t.Error("want method POST for PushAddCollectors, got", lastMethod) 121 } 122 if bytes.Compare(lastBody, wantBody) != 0 { 123 t.Errorf("got body %v, want %v", lastBody, wantBody) 124 } 125 if lastPath != "/metrics/job/testjob" { 126 t.Error("unexpected path:", lastPath) 127 } 128 129 // PushCollectors with a broken PGW. 130 if err := Collectors("testjob", nil, pgwErr.URL, metric1, metric2); err == nil { 131 t.Error("push to broken Pushgateway succeeded") 132 } else { 133 if got, want := err.Error(), "unexpected status code 500 while pushing to "+pgwErr.URL+"/metrics/job/testjob: fake error\n"; got != want { 134 t.Errorf("got error %q, want %q", got, want) 135 } 136 } 137 138 // PushCollectors with invalid grouping or job. 139 if err := Collectors("testjob", map[string]string{"foo": "bums"}, pgwErr.URL, metric1, metric2); err == nil { 140 t.Error("push with grouping contained in metrics succeeded") 141 } 142 if err := Collectors("test/job", nil, pgwErr.URL, metric1, metric2); err == nil { 143 t.Error("push with invalid job value succeeded") 144 } 145 if err := Collectors("testjob", map[string]string{"foo/bar": "bums"}, pgwErr.URL, metric1, metric2); err == nil { 146 t.Error("push with invalid grouping succeeded") 147 } 148 if err := Collectors("testjob", map[string]string{"foo-bar": "bums"}, pgwErr.URL, metric1, metric2); err == nil { 149 t.Error("push with invalid grouping succeeded") 150 } 151 152 // Push registry, all good. 153 if err := FromGatherer("testjob", HostnameGroupingKey(), pgwOK.URL, reg); err != nil { 154 t.Fatal(err) 155 } 156 if lastMethod != "PUT" { 157 t.Error("want method PUT for Push, got", lastMethod) 158 } 159 if bytes.Compare(lastBody, wantBody) != 0 { 160 t.Errorf("got body %v, want %v", lastBody, wantBody) 161 } 162 163 // PushAdd registry, all good. 164 if err := AddFromGatherer("testjob", map[string]string{"a": "x", "b": "y"}, pgwOK.URL, reg); err != nil { 165 t.Fatal(err) 166 } 167 if lastMethod != "POST" { 168 t.Error("want method POSTT for PushAdd, got", lastMethod) 169 } 170 if bytes.Compare(lastBody, wantBody) != 0 { 171 t.Errorf("got body %v, want %v", lastBody, wantBody) 172 } 173 if lastPath != "/metrics/job/testjob/a/x/b/y" && lastPath != "/metrics/job/testjob/b/y/a/x" { 174 t.Error("unexpected path:", lastPath) 175 } 176} 177