2022-07-20 16:53:12 +03:00
|
|
|
package metrics
|
|
|
|
|
|
|
|
import (
|
2023-12-17 18:23:33 +02:00
|
|
|
"bytes"
|
|
|
|
"compress/gzip"
|
|
|
|
"context"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2024-01-15 12:07:18 +02:00
|
|
|
"sync"
|
2022-07-20 16:53:12 +03:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestAddExtraLabels(t *testing.T) {
|
|
|
|
f := func(s, extraLabels, expectedResult string) {
|
|
|
|
t.Helper()
|
|
|
|
result := addExtraLabels(nil, []byte(s), extraLabels)
|
|
|
|
if string(result) != expectedResult {
|
|
|
|
t.Fatalf("unexpected result; got\n%s\nwant\n%s", result, expectedResult)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f("", `foo="bar"`, "")
|
|
|
|
f("a 123", `foo="bar"`, `a{foo="bar"} 123`+"\n")
|
|
|
|
f(`a{b="c"} 1.3`, `foo="bar"`, `a{foo="bar",b="c"} 1.3`+"\n")
|
|
|
|
f(`a{b="c}{"} 1.3`, `foo="bar",baz="x"`, `a{foo="bar",baz="x",b="c}{"} 1.3`+"\n")
|
|
|
|
f(`foo 1
|
|
|
|
bar{a="x"} 2
|
|
|
|
`, `foo="bar"`, `foo{foo="bar"} 1
|
|
|
|
bar{foo="bar",a="x"} 2
|
2022-07-21 18:35:46 +03:00
|
|
|
`)
|
|
|
|
f(`
|
|
|
|
foo 1
|
|
|
|
# some counter
|
|
|
|
# type foobar counter
|
2022-07-21 18:42:53 +03:00
|
|
|
foobar{a="b",c="d"} 4`, `x="y"`, `foo{x="y"} 1
|
2022-07-21 18:35:46 +03:00
|
|
|
# some counter
|
|
|
|
# type foobar counter
|
|
|
|
foobar{x="y",a="b",c="d"} 4
|
2022-07-20 16:53:12 +03:00
|
|
|
`)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInitPushFailure(t *testing.T) {
|
2022-07-21 19:45:07 +03:00
|
|
|
f := func(pushURL string, interval time.Duration, extraLabels string) {
|
2022-07-20 16:53:12 +03:00
|
|
|
t.Helper()
|
2022-07-21 19:45:07 +03:00
|
|
|
if err := InitPush(pushURL, interval, extraLabels, false); err == nil {
|
|
|
|
t.Fatalf("expecting non-nil error")
|
|
|
|
}
|
2022-07-20 16:53:12 +03:00
|
|
|
}
|
|
|
|
|
2022-07-21 19:45:07 +03:00
|
|
|
// Invalid url
|
|
|
|
f("foobar", time.Second, "")
|
|
|
|
f("aaa://foobar", time.Second, "")
|
|
|
|
f("http:///bar", time.Second, "")
|
|
|
|
|
2022-07-20 16:53:12 +03:00
|
|
|
// Non-positive interval
|
2022-07-21 19:45:07 +03:00
|
|
|
f("http://foobar", 0, "")
|
|
|
|
f("http://foobar", -time.Second, "")
|
2022-07-20 16:53:12 +03:00
|
|
|
|
|
|
|
// Invalid extraLabels
|
2022-07-21 19:45:07 +03:00
|
|
|
f("http://foobar", time.Second, "foo")
|
|
|
|
f("http://foobar", time.Second, "foo{bar")
|
|
|
|
f("http://foobar", time.Second, "foo=bar")
|
|
|
|
f("http://foobar", time.Second, "foo='bar'")
|
|
|
|
f("http://foobar", time.Second, `foo="bar",baz`)
|
|
|
|
f("http://foobar", time.Second, `{foo="bar"}`)
|
|
|
|
f("http://foobar", time.Second, `a{foo="bar"}`)
|
2022-07-20 16:53:12 +03:00
|
|
|
}
|
2023-12-17 18:23:33 +02:00
|
|
|
|
|
|
|
func TestInitPushWithOptions(t *testing.T) {
|
|
|
|
f := func(s *Set, opts *PushOptions, expectedHeaders, expectedData string) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
var reqHeaders []byte
|
|
|
|
var reqData []byte
|
|
|
|
var reqErr error
|
|
|
|
doneCh := make(chan struct{})
|
|
|
|
firstRequest := true
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if firstRequest {
|
|
|
|
var bb bytes.Buffer
|
|
|
|
r.Header.WriteSubset(&bb, map[string]bool{
|
|
|
|
"Accept-Encoding": true,
|
|
|
|
"Content-Length": true,
|
|
|
|
"User-Agent": true,
|
|
|
|
})
|
|
|
|
reqHeaders = bb.Bytes()
|
|
|
|
reqData, reqErr = io.ReadAll(r.Body)
|
|
|
|
close(doneCh)
|
|
|
|
firstRequest = false
|
|
|
|
}
|
|
|
|
}))
|
2023-12-18 12:35:00 +02:00
|
|
|
defer srv.Close()
|
2023-12-17 18:23:33 +02:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
2024-01-15 12:07:18 +02:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
if opts != nil {
|
|
|
|
opts.WaitGroup = &wg
|
|
|
|
}
|
2023-12-17 18:23:33 +02:00
|
|
|
if err := s.InitPushWithOptions(ctx, srv.URL, time.Millisecond, opts); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-time.After(5 * time.Second):
|
|
|
|
t.Fatalf("timeout!")
|
|
|
|
case <-doneCh:
|
|
|
|
// stop the periodic pusher
|
|
|
|
cancel()
|
2024-01-15 12:07:18 +02:00
|
|
|
wg.Wait()
|
2023-12-17 18:23:33 +02:00
|
|
|
}
|
|
|
|
if reqErr != nil {
|
|
|
|
t.Fatalf("unexpected error: %s", reqErr)
|
|
|
|
}
|
|
|
|
if opts == nil || !opts.DisableCompression {
|
|
|
|
zr, err := gzip.NewReader(bytes.NewBuffer(reqData))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("cannot initialize gzip reader: %s", err)
|
|
|
|
}
|
|
|
|
data, err := io.ReadAll(zr)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("cannot read data from gzip reader: %s", err)
|
|
|
|
}
|
|
|
|
if err := zr.Close(); err != nil {
|
|
|
|
t.Fatalf("unexpected error when closing gzip reader: %s", err)
|
|
|
|
}
|
|
|
|
reqData = data
|
|
|
|
}
|
|
|
|
if string(reqHeaders) != expectedHeaders {
|
|
|
|
t.Fatalf("unexpected request headers; got\n%s\nwant\n%s", reqHeaders, expectedHeaders)
|
2023-12-19 00:18:38 +02:00
|
|
|
}
|
|
|
|
if string(reqData) != expectedData {
|
|
|
|
t.Fatalf("unexpected data; got\n%s\nwant\n%s", reqData, expectedData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s := NewSet()
|
|
|
|
c := s.NewCounter("foo")
|
|
|
|
c.Set(1234)
|
|
|
|
_ = s.NewGauge("bar", func() float64 {
|
|
|
|
return 42.12
|
|
|
|
})
|
|
|
|
|
|
|
|
// nil PushOptions
|
|
|
|
f(s, nil, "Content-Encoding: gzip\r\nContent-Type: text/plain\r\n", "bar 42.12\nfoo 1234\n")
|
|
|
|
|
|
|
|
// Disable compression on the pushed request body
|
|
|
|
f(s, &PushOptions{
|
|
|
|
DisableCompression: true,
|
|
|
|
}, "Content-Type: text/plain\r\n", "bar 42.12\nfoo 1234\n")
|
|
|
|
|
|
|
|
// Add extra labels
|
|
|
|
f(s, &PushOptions{
|
|
|
|
ExtraLabels: `label1="value1",label2="value2"`,
|
|
|
|
}, "Content-Encoding: gzip\r\nContent-Type: text/plain\r\n", `bar{label1="value1",label2="value2"} 42.12`+"\n"+`foo{label1="value1",label2="value2"} 1234`+"\n")
|
|
|
|
|
|
|
|
// Add extra headers
|
|
|
|
f(s, &PushOptions{
|
|
|
|
Headers: []string{"Foo: Bar", "baz:aaaa-bbb"},
|
|
|
|
}, "Baz: aaaa-bbb\r\nContent-Encoding: gzip\r\nContent-Type: text/plain\r\nFoo: Bar\r\n", "bar 42.12\nfoo 1234\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPushMetrics(t *testing.T) {
|
|
|
|
f := func(s *Set, opts *PushOptions, expectedHeaders, expectedData string) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
var reqHeaders []byte
|
|
|
|
var reqData []byte
|
|
|
|
var reqErr error
|
|
|
|
doneCh := make(chan struct{})
|
|
|
|
firstRequest := true
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if firstRequest {
|
|
|
|
var bb bytes.Buffer
|
|
|
|
r.Header.WriteSubset(&bb, map[string]bool{
|
|
|
|
"Accept-Encoding": true,
|
|
|
|
"Content-Length": true,
|
|
|
|
"User-Agent": true,
|
|
|
|
})
|
|
|
|
reqHeaders = bb.Bytes()
|
|
|
|
reqData, reqErr = io.ReadAll(r.Body)
|
|
|
|
close(doneCh)
|
|
|
|
firstRequest = false
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
defer srv.Close()
|
|
|
|
ctx := context.Background()
|
|
|
|
if err := s.PushMetrics(ctx, srv.URL, opts); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %s", err)
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-time.After(5 * time.Second):
|
|
|
|
t.Fatalf("timeout!")
|
|
|
|
case <-doneCh:
|
|
|
|
}
|
|
|
|
if reqErr != nil {
|
|
|
|
t.Fatalf("unexpected error: %s", reqErr)
|
|
|
|
}
|
|
|
|
if opts == nil || !opts.DisableCompression {
|
|
|
|
zr, err := gzip.NewReader(bytes.NewBuffer(reqData))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("cannot initialize gzip reader: %s", err)
|
|
|
|
}
|
|
|
|
data, err := io.ReadAll(zr)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("cannot read data from gzip reader: %s", err)
|
|
|
|
}
|
|
|
|
if err := zr.Close(); err != nil {
|
|
|
|
t.Fatalf("unexpected error when closing gzip reader: %s", err)
|
|
|
|
}
|
|
|
|
reqData = data
|
|
|
|
}
|
|
|
|
if string(reqHeaders) != expectedHeaders {
|
|
|
|
t.Fatalf("unexpected request headers; got\n%s\nwant\n%s", reqHeaders, expectedHeaders)
|
2023-12-17 18:23:33 +02:00
|
|
|
}
|
|
|
|
if string(reqData) != expectedData {
|
|
|
|
t.Fatalf("unexpected data; got\n%s\nwant\n%s", reqData, expectedData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s := NewSet()
|
|
|
|
c := s.NewCounter("foo")
|
|
|
|
c.Set(1234)
|
|
|
|
_ = s.NewGauge("bar", func() float64 {
|
|
|
|
return 42.12
|
|
|
|
})
|
|
|
|
|
|
|
|
// nil PushOptions
|
|
|
|
f(s, nil, "Content-Encoding: gzip\r\nContent-Type: text/plain\r\n", "bar 42.12\nfoo 1234\n")
|
|
|
|
|
|
|
|
// Disable compression on the pushed request body
|
|
|
|
f(s, &PushOptions{
|
|
|
|
DisableCompression: true,
|
|
|
|
}, "Content-Type: text/plain\r\n", "bar 42.12\nfoo 1234\n")
|
|
|
|
|
|
|
|
// Add extra labels
|
|
|
|
f(s, &PushOptions{
|
|
|
|
ExtraLabels: `label1="value1",label2="value2"`,
|
|
|
|
}, "Content-Encoding: gzip\r\nContent-Type: text/plain\r\n", `bar{label1="value1",label2="value2"} 42.12`+"\n"+`foo{label1="value1",label2="value2"} 1234`+"\n")
|
|
|
|
|
|
|
|
// Add extra headers
|
|
|
|
f(s, &PushOptions{
|
|
|
|
Headers: []string{"Foo: Bar", "baz:aaaa-bbb"},
|
|
|
|
}, "Baz: aaaa-bbb\r\nContent-Encoding: gzip\r\nContent-Type: text/plain\r\nFoo: Bar\r\n", "bar 42.12\nfoo 1234\n")
|
|
|
|
}
|