diff --git a/clients/pkg/promtail/targets/gelf/gelftarget.go b/clients/pkg/promtail/targets/gelf/gelftarget.go index e0032531c7fd9..d27ce7d0eb5e8 100644 --- a/clients/pkg/promtail/targets/gelf/gelftarget.go +++ b/clients/pkg/promtail/targets/gelf/gelftarget.go @@ -9,10 +9,10 @@ import ( "github.com/go-kit/log" "github.com/go-kit/log/level" + "github.com/grafana/go-gelf/v2/gelf" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/relabel" - "gopkg.in/Graylog2/go-gelf.v2/gelf" "github.com/grafana/loki/clients/pkg/promtail/api" "github.com/grafana/loki/clients/pkg/promtail/scrapeconfig" diff --git a/clients/pkg/promtail/targets/gelf/gelftarget_test.go b/clients/pkg/promtail/targets/gelf/gelftarget_test.go index fdbb4f04f984a..86a304ef9a7a0 100644 --- a/clients/pkg/promtail/targets/gelf/gelftarget_test.go +++ b/clients/pkg/promtail/targets/gelf/gelftarget_test.go @@ -1,14 +1,19 @@ package gelf import ( + "crypto/rand" + "fmt" + "io" + "net" + "strings" "testing" "time" "github.com/go-kit/log" + "github.com/grafana/go-gelf/v2/gelf" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/relabel" "github.com/stretchr/testify/require" - "gopkg.in/Graylog2/go-gelf.v2/gelf" "github.com/grafana/loki/clients/pkg/promtail/client/fake" "github.com/grafana/loki/clients/pkg/promtail/scrapeconfig" @@ -16,12 +21,11 @@ import ( func Test_Gelf(t *testing.T) { client := fake.New(func() {}) - tm, err := NewTargetManager(NewMetrics(nil), log.NewNopLogger(), client, []scrapeconfig.Config{ { JobName: "gelf", GelfConfig: &scrapeconfig.GelfTargetConfig{ - ListenAddress: ":12201", + ListenAddress: ":0", UseIncomingTimestamp: true, Labels: model.LabelSet{"cfg": "true"}, }, @@ -51,9 +55,14 @@ func Test_Gelf(t *testing.T) { }, }) require.NoError(t, err) - - w, err := gelf.NewUDPWriter(":12201") + defer tm.Stop() + target := tm.targets["gelf"] + require.NotNil(t, target) + w, err := gelf.NewUDPWriter(target.gelfReader.Addr()) require.NoError(t, err) + defer func() { + require.NoError(t, w.Close()) + }() baseTs := float64(time.Unix(10, 0).Unix()) + 0.250 ts := baseTs @@ -75,7 +84,7 @@ func Test_Gelf(t *testing.T) { require.Eventually(t, func() bool { return len(client.Received()) == 10 - }, 200*time.Millisecond, 20*time.Millisecond) + }, 1*time.Second, 20*time.Millisecond) for i, actual := range client.Received() { require.Equal(t, "error", string(actual.Labels["level"])) @@ -98,10 +107,95 @@ func Test_Gelf(t *testing.T) { require.Equal(t, "gelftest", gelfMsg.Facility) } - - tm.Stop() } func TestConvertTime(t *testing.T) { require.Equal(t, time.Unix(0, int64(time.Second+(time.Duration(250)*time.Millisecond))), secondsToUnixTimestamp(float64(time.Unix(1, 0).Unix())+0.250)) } + +func Test_GelfChunksUnordered(t *testing.T) { + client := fake.New(func() {}) + + tm, err := NewTargetManager(NewMetrics(nil), log.NewNopLogger(), client, []scrapeconfig.Config{ + { + JobName: "gelf", + GelfConfig: &scrapeconfig.GelfTargetConfig{ + ListenAddress: ":0", + }, + }, + }) + require.NoError(t, err) + defer tm.Stop() + + target := tm.targets["gelf"] + require.NotNil(t, target) + connection, err := net.Dial("udp", target.gelfReader.Addr()) + require.NoError(t, err) + defer func() { + require.NoError(t, connection.Close()) + }() + + chunksA := createChunks(t, "a") + chunksB := createChunks(t, "b") + // send messages(a, b) chunks in order: chunk-0a, chunk-0b, chunk-1a, chunk-1b + for i := 0; i < len(chunksB); i++ { + writeA, err := connection.Write(chunksA[i]) + require.NoError(t, err) + require.Equal(t, len(chunksA[i]), writeA) + + writeB, err := connection.Write(chunksB[i]) + require.NoError(t, err) + require.Equal(t, len(chunksB[i]), writeB) + } + + require.Eventually(t, func() bool { + return len(client.Received()) == 2 + }, 2*time.Second, 100*time.Millisecond, "expected 2 messages to be received") +} + +func createChunks(t *testing.T, char string) [][]byte { + chunksA, err := splitToChunks([]byte(fmt.Sprintf("{\"short_message\":\"%v\"}", strings.Repeat(char, gelf.ChunkSize*2)))) + require.NoError(t, err) + return chunksA +} + +// static value that indicated that GELF message is chunked +var magicChunked = []byte{0x1e, 0x0f} + +const ( + chunkedHeaderLen = 12 + chunkedDataLen = gelf.ChunkSize - chunkedHeaderLen +) + +func splitToChunks(messageBytes []byte) ([][]byte, error) { + chunksCount := uint8(len(messageBytes)/chunkedDataLen + 1) + messageID := make([]byte, 8) + n, err := io.ReadFull(rand.Reader, messageID) + if err != nil || n != 8 { + return nil, fmt.Errorf("rand.Reader: %d/%s", n, err) + } + chunks := make([][]byte, 0, chunksCount) + bytesLeft := len(messageBytes) + for i := uint8(0); i < chunksCount; i++ { + buf := make([]byte, 0, gelf.ChunkSize) + buf = append(buf, magicChunked...) + buf = append(buf, messageID...) + buf = append(buf, i) + buf = append(buf, chunksCount) + chunkLen := chunkedDataLen + if chunkLen > bytesLeft { + chunkLen = bytesLeft + } + off := int(i) * chunkedDataLen + chunkData := messageBytes[off : off+chunkLen] + buf = append(buf, chunkData...) + + chunks = append(chunks, buf) + bytesLeft -= chunkLen + } + + if bytesLeft != 0 { + return nil, fmt.Errorf("error: %d bytes left after splitting", bytesLeft) + } + return chunks, nil +} diff --git a/go.mod b/go.mod index ec04c64b10ca4..f1ca8b19072ec 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 github.com/grafana/dskit v0.0.0-20220331160727-49faf69f72ca + github.com/grafana/go-gelf/v2 v2.0.1 github.com/grafana/regexp v0.0.0-20220304100321-149c8afcd6cb github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 @@ -85,7 +86,7 @@ require ( github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 github.com/sony/gobreaker v0.4.1 github.com/spf13/afero v1.6.0 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.1 github.com/thanos-io/thanos v0.22.0 github.com/tonistiigi/fifo v0.0.0-20190226154929-a9fb20d87448 github.com/uber/jaeger-client-go v2.30.0+incompatible @@ -101,7 +102,6 @@ require ( golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 google.golang.org/api v0.70.0 google.golang.org/grpc v1.44.0 - gopkg.in/Graylog2/go-gelf.v2 v2.0.0-20191017102106-1550ee647df0 gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/fsnotify.v1 v1.4.7 gopkg.in/yaml.v2 v2.4.0 @@ -311,7 +311,4 @@ replace github.com/bradfitz/gomemcache => github.com/themihai/gomemcache v0.0.0- // is v0.19.1. We pin version from late september here. Feel free to remove when updating to later version. replace github.com/thanos-io/thanos v0.22.0 => github.com/thanos-io/thanos v0.19.1-0.20211126105533-c5505f5eaa7d -// We use a fork of Graylog to avoid leaking goroutine when closing the Promtail target. -replace gopkg.in/Graylog2/go-gelf.v2 => github.com/grafana/go-gelf v0.0.0-20211112153804-126646b86de8 - replace github.com/cloudflare/cloudflare-go => github.com/cyriltovena/cloudflare-go v0.27.1-0.20211118103540-ff77400bcb93 diff --git a/go.sum b/go.sum index 0224659d21b11..5e258309f8b49 100644 --- a/go.sum +++ b/go.sum @@ -1037,8 +1037,8 @@ github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM github.com/grafana/dskit v0.0.0-20211021180445-3bd016e9d7f1/go.mod h1:uPG2nyK4CtgNDmWv7qyzYcdI+S90kHHRWvHnBtEMBXM= github.com/grafana/dskit v0.0.0-20220331160727-49faf69f72ca h1:0qHzm6VS0bCsSWKHuyfpt+pdpyScdZbzY/IFIyKSYOk= github.com/grafana/dskit v0.0.0-20220331160727-49faf69f72ca/go.mod h1:q51XdMLLHNZJSG6KOGujC20ed2OoLFdx0hBmOEVfRs0= -github.com/grafana/go-gelf v0.0.0-20211112153804-126646b86de8 h1:aEOagXOTqtN9gd4jiDuP/5a81HdoJBqkVfn8WaxbsK4= -github.com/grafana/go-gelf v0.0.0-20211112153804-126646b86de8/go.mod h1:QAvS2C7TtQRhhv9Uf/sxD+BUhpkrPFm5jK/9MzUiDCY= +github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= +github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= github.com/grafana/gocql v0.0.0-20200605141915-ba5dc39ece85 h1:xLuzPoOzdfNb/RF/IENCw+oLVdZB4G21VPhkHBgwSHY= github.com/grafana/gocql v0.0.0-20200605141915-ba5dc39ece85/go.mod h1:crI9WX6p0IhrqB+DqIUHulRW853PaNFf7o4UprV//3I= github.com/grafana/regexp v0.0.0-20220202152315-e74e38789280/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= @@ -1820,8 +1820,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= diff --git a/vendor/gopkg.in/Graylog2/go-gelf.v2/LICENSE b/vendor/github.com/grafana/go-gelf/v2/LICENSE similarity index 100% rename from vendor/gopkg.in/Graylog2/go-gelf.v2/LICENSE rename to vendor/github.com/grafana/go-gelf/v2/LICENSE diff --git a/vendor/github.com/grafana/go-gelf/v2/gelf/defragmentator.go b/vendor/github.com/grafana/go-gelf/v2/gelf/defragmentator.go new file mode 100644 index 0000000000000..f19c722f1cd87 --- /dev/null +++ b/vendor/github.com/grafana/go-gelf/v2/gelf/defragmentator.go @@ -0,0 +1,53 @@ +package gelf + +import ( + "bytes" + "time" +) + +// defragmentator provides GELF message de-chunking. +type defragmentator struct { + deadline time.Time + chunksData [][]byte + processed int + totalBytes int +} + +// newDefragmentator returns empty defragmentator with maximum message size and duration +// specified. +func newDefragmentator(timeout time.Duration) (res *defragmentator) { + res = new(defragmentator) + res.deadline = time.Now().Add(timeout) + return +} + +// bytes returns message bytes, not nessesarily fully defragmentated. +func (a *defragmentator) bytes() []byte { + return bytes.Join(a.chunksData, nil) +} + +// expired returns true if first chunk is too old. +func (a *defragmentator) expired() bool { + return time.Now().After(a.deadline) +} + +// update feeds the byte chunk to defragmentator, returns true when the message is +// complete. +func (a *defragmentator) update(chunk []byte) bool { + // each message contains index of current chunk and total count of chunks + chunkIndex, count := int(chunk[10]), int(chunk[11]) + if a.chunksData == nil { + a.chunksData = make([][]byte, count) + } + if count != len(a.chunksData) || chunkIndex >= count { + return false + } + body := chunk[chunkedHeaderLen:] + if a.chunksData[chunkIndex] == nil { + chunkData := make([]byte, 0, len(body)) + a.totalBytes += len(body) + a.chunksData[chunkIndex] = append(chunkData, body...) + a.processed++ + } + return a.processed == len(a.chunksData) +} diff --git a/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/message.go b/vendor/github.com/grafana/go-gelf/v2/gelf/message.go similarity index 100% rename from vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/message.go rename to vendor/github.com/grafana/go-gelf/v2/gelf/message.go diff --git a/vendor/github.com/grafana/go-gelf/v2/gelf/reader.go b/vendor/github.com/grafana/go-gelf/v2/gelf/reader.go new file mode 100644 index 0000000000000..ca4c9b203f39b --- /dev/null +++ b/vendor/github.com/grafana/go-gelf/v2/gelf/reader.go @@ -0,0 +1,198 @@ +// Copyright 2012 SocialCode. All rights reserved. +// Use of this source code is governed by the MIT +// license that can be found in the LICENSE file. + +package gelf + +import ( + "bytes" + "compress/gzip" + "compress/zlib" + "encoding/json" + "fmt" + "io" + "net" + "strings" + "sync" + "time" +) + +const ( + //according to Gelf specification, all chunks must be received within 5 seconds. see:https://docs.graylog.org/docs/gelf + maxMessageTimeout = 5 * time.Second + defarmentatorsCleanUpInterval = 5 * time.Second +) + +type Reader struct { + mu sync.Mutex + conn net.Conn + messageDefragmentators map[string]*defragmentator + done chan struct{} + maxMessageTimeout time.Duration +} + +// NewReader creates a reader for specified addr +func NewReader(addr string) (*Reader, error) { + return newReader(addr, maxMessageTimeout, defarmentatorsCleanUpInterval) +} + +func newReader(addr string, maxMessageTimeout time.Duration, defarmentatorsCleanUpInterval time.Duration) (*Reader, error) { + var err error + udpAddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return nil, fmt.Errorf("ResolveUDPAddr('%s'): %s", addr, err) + } + + conn, err := net.ListenUDP("udp", udpAddr) + if err != nil { + return nil, fmt.Errorf("ListenUDP: %s", err) + } + + r := new(Reader) + r.maxMessageTimeout = maxMessageTimeout + r.conn = conn + r.messageDefragmentators = make(map[string]*defragmentator) + r.done = make(chan struct{}) + r.initDefragmentatorsCleanup(defarmentatorsCleanUpInterval) + return r, nil +} + +func (r *Reader) Addr() string { + return r.conn.LocalAddr().String() +} + +// FIXME: this will discard data if p isn't big enough to hold the +// full message. +func (r *Reader) Read(p []byte) (int, error) { + msg, err := r.ReadMessage() + if err != nil { + return -1, err + } + + var data string + + if msg.Full == "" { + data = msg.Short + } else { + data = msg.Full + } + + return strings.NewReader(data).Read(p) +} + +// ReadMessage reads the message from connection. +// +// If reader receives a message that is not chunked, the reader returns it immediately. +// +// If reader receives a message that is chunked, the reader finds or creates defragmentator by messageID and adds the message to the defragmentator +// and continue reading the message from the connection until it receives the last chunk of any previously received messages or if the reader receives not chunked message. +// In this case, not chunked message will be returned immediately. +func (r *Reader) ReadMessage() (*Message, error) { + cBuf := make([]byte, ChunkSize) + var ( + err error + n int + chunkHead []byte + cReader io.Reader + ) + var message []byte + + for { + // we need to reset buffer length because we change the length of the buffer to `n` after the reading data from connection + cBuf = cBuf[:ChunkSize] + if n, err = r.conn.Read(cBuf); err != nil { + return nil, fmt.Errorf("Read: %s", err) + } + // first two bytes contains hardcoded values [0x1e, 0x0f] if message is chunked + chunkHead, cBuf = cBuf[:2], cBuf[:n] + if bytes.Equal(chunkHead, magicChunked) { + if len(cBuf) <= chunkedHeaderLen { + return nil, fmt.Errorf("chunked message size must be greather than %v", chunkedHeaderLen) + } + // in chunked message, message id is 8 bytes after the message head + messageID := string(cBuf[2 : 2+8]) + deframentator := getDeframentator(r, messageID) + if deframentator.processed >= maxChunksCount { + return nil, fmt.Errorf("message must not be split into more than %v chunks", maxChunksCount) + } + if deframentator.expired() { + return nil, fmt.Errorf("message with ID: %v is expired", messageID) + } + if messageCompleted := deframentator.update(cBuf); !messageCompleted { + continue + } + message = deframentator.bytes() + chunkHead = message[:2] + r.mu.Lock() + delete(r.messageDefragmentators, messageID) + r.mu.Unlock() + } else { + message = cBuf + } + break + } + + // the data we get from the wire is compressed + if bytes.Equal(chunkHead, magicGzip) { + cReader, err = gzip.NewReader(bytes.NewReader(message)) + } else if chunkHead[0] == magicZlib[0] && + (int(chunkHead[0])*256+int(chunkHead[1]))%31 == 0 { + // zlib is slightly more complicated, but correct + cReader, err = zlib.NewReader(bytes.NewReader(message)) + } else { + // compliance with https://github.com/Graylog2/graylog2-server + // treating all messages as uncompressed if they are not gzip, zlib or + // chunked + cReader = bytes.NewReader(message) + } + + if err != nil { + return nil, fmt.Errorf("NewReader: %s", err) + } + + msg := new(Message) + if err := json.NewDecoder(cReader).Decode(&msg); err != nil { + return nil, fmt.Errorf("json.Unmarshal: %s", err) + } + + return msg, nil +} + +func getDeframentator(r *Reader, messageID string) *defragmentator { + r.mu.Lock() + defer r.mu.Unlock() + defragmentator, ok := r.messageDefragmentators[messageID] + if !ok { + defragmentator = newDefragmentator(r.maxMessageTimeout) + r.messageDefragmentators[messageID] = defragmentator + } + return defragmentator +} + +func (r *Reader) Close() error { + close(r.done) + return r.conn.Close() +} + +func (r *Reader) initDefragmentatorsCleanup(defarmentatorsCleanUpInterval time.Duration) { + go func() { + for { + select { + case <-r.done: + return + case <-time.After(defarmentatorsCleanUpInterval): + r.cleanUpExpiredDefragmentators() + } + } + }() +} + +func (r *Reader) cleanUpExpiredDefragmentators() { + r.mu.Lock() + defer r.mu.Unlock() + for messageID, defragmentator := range r.messageDefragmentators { + if defragmentator.expired() { + delete(r.messageDefragmentators, messageID) + } + } +} diff --git a/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/tcpreader.go b/vendor/github.com/grafana/go-gelf/v2/gelf/tcpreader.go similarity index 100% rename from vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/tcpreader.go rename to vendor/github.com/grafana/go-gelf/v2/gelf/tcpreader.go diff --git a/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/tcpwriter.go b/vendor/github.com/grafana/go-gelf/v2/gelf/tcpwriter.go similarity index 100% rename from vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/tcpwriter.go rename to vendor/github.com/grafana/go-gelf/v2/gelf/tcpwriter.go diff --git a/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/udpwriter.go b/vendor/github.com/grafana/go-gelf/v2/gelf/udpwriter.go similarity index 94% rename from vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/udpwriter.go rename to vendor/github.com/grafana/go-gelf/v2/gelf/udpwriter.go index 23bbd5e510eb0..95061344f2626 100644 --- a/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/udpwriter.go +++ b/vendor/github.com/grafana/go-gelf/v2/gelf/udpwriter.go @@ -42,6 +42,9 @@ const ( ChunkSize = 1420 chunkedHeaderLen = 12 chunkedDataLen = ChunkSize - chunkedHeaderLen + // maxChunksCount is limited by the protocol to a maximum of 128 + // https://docs.graylog.org/docs/gelf#gelf-via-udp + maxChunksCount = 128 ) var ( @@ -90,8 +93,8 @@ func (w *GelfWriter) writeChunked(zBytes []byte) (err error) { b := make([]byte, 0, ChunkSize) buf := bytes.NewBuffer(b) nChunksI := numChunks(zBytes) - if nChunksI > 128 { - return fmt.Errorf("msg too large, would need %d chunks", nChunksI) + if nChunksI > maxChunksCount { + return fmt.Errorf("msg too large, would need %d chunks which execeeds the limit of %d chunks", nChunksI, maxChunksCount) } nChunks := uint8(nChunksI) // use urandom to get a unique message id diff --git a/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/utils.go b/vendor/github.com/grafana/go-gelf/v2/gelf/utils.go similarity index 100% rename from vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/utils.go rename to vendor/github.com/grafana/go-gelf/v2/gelf/utils.go diff --git a/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/writer.go b/vendor/github.com/grafana/go-gelf/v2/gelf/writer.go similarity index 100% rename from vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/writer.go rename to vendor/github.com/grafana/go-gelf/v2/gelf/writer.go diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 41649d2679246..3bb22a9718eb8 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -3,6 +3,7 @@ package assert import ( "fmt" "reflect" + "time" ) type CompareType int @@ -30,6 +31,8 @@ var ( float64Type = reflect.TypeOf(float64(1)) stringType = reflect.TypeOf("") + + timeType = reflect.TypeOf(time.Time{}) ) func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { @@ -299,6 +302,27 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { return compareLess, true } } + // Check for known struct types we can check for compare results. + case reflect.Struct: + { + // All structs enter here. We're not interested in most types. + if !canConvert(obj1Value, timeType) { + break + } + + // time.Time can compared! + timeObj1, ok := obj1.(time.Time) + if !ok { + timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time) + } + + timeObj2, ok := obj2.(time.Time) + if !ok { + timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) + } + + return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64) + } } return compareEqual, false @@ -310,7 +334,10 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { // assert.Greater(t, float64(2), float64(1)) // assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second @@ -320,7 +347,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // assert.GreaterOrEqual(t, "b", "a") // assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // Less asserts that the first element is less than the second @@ -329,7 +359,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // assert.Less(t, float64(1), float64(2)) // assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second @@ -339,7 +372,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // assert.LessOrEqual(t, "a", "b") // assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } // Positive asserts that the specified element is positive @@ -347,8 +383,11 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // assert.Positive(t, 1) // assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...) } // Negative asserts that the specified element is negative @@ -356,8 +395,11 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { // assert.Negative(t, -1) // assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...) } func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go new file mode 100644 index 0000000000000..df22c47fc50aa --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go @@ -0,0 +1,16 @@ +//go:build go1.17 +// +build go1.17 + +// TODO: once support for Go 1.16 is dropped, this file can be +// merged/removed with assertion_compare_go1.17_test.go and +// assertion_compare_legacy.go + +package assert + +import "reflect" + +// Wrapper around reflect.Value.CanConvert, for compatability +// reasons. +func canConvert(value reflect.Value, to reflect.Type) bool { + return value.CanConvert(to) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go new file mode 100644 index 0000000000000..1701af2a3c89c --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go @@ -0,0 +1,16 @@ +//go:build !go1.17 +// +build !go1.17 + +// TODO: once support for Go 1.16 is dropped, this file can be +// merged/removed with assertion_compare_go1.17_test.go and +// assertion_compare_can_convert.go + +package assert + +import "reflect" + +// Older versions of Go does not have the reflect.Value.CanConvert +// method. +func canConvert(value reflect.Value, to reflect.Type) bool { + return false +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 4dfd1229a8617..27e2420ed2e76 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -123,6 +123,18 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) } +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...) +} + // ErrorIsf asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 25337a6f07e6e..d9ea368d0a355 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -222,6 +222,30 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. return ErrorAsf(a.t, err, target, msg, args...) } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) +func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorContainsf(a.t, theError, contains, msg, args...) +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 1c3b47182a726..7594487835856 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -50,7 +50,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing @@ -59,7 +59,7 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing @@ -68,7 +68,7 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing @@ -77,5 +77,5 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index bcac4401f57fb..0357b2231a2cd 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -718,10 +718,14 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte // return (false, false) if impossible. // return (true, false) if element was not found. // return (true, true) if element was found. -func includeElement(list interface{}, element interface{}) (ok, found bool) { +func containsElement(list interface{}, element interface{}) (ok, found bool) { listValue := reflect.ValueOf(list) - listKind := reflect.TypeOf(list).Kind() + listType := reflect.TypeOf(list) + if listType == nil { + return false, false + } + listKind := listType.Kind() defer func() { if e := recover(); e != nil { ok = false @@ -764,7 +768,7 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo h.Helper() } - ok, found := includeElement(s, contains) + ok, found := containsElement(s, contains) if !ok { return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } @@ -787,7 +791,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) h.Helper() } - ok, found := includeElement(s, contains) + ok, found := containsElement(s, contains) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) } @@ -831,7 +835,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() - ok, found := includeElement(list, element) + ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) } @@ -852,7 +856,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) h.Helper() } if subset == nil { - return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...) + return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } subsetValue := reflect.ValueOf(subset) @@ -875,7 +879,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() - ok, found := includeElement(list, element) + ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) } @@ -1000,27 +1004,21 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { type PanicTestFunc func() // didPanic returns true if the function passed to it panics. Otherwise, it returns false. -func didPanic(f PanicTestFunc) (bool, interface{}, string) { - - didPanic := false - var message interface{} - var stack string - func() { - - defer func() { - if message = recover(); message != nil { - didPanic = true - stack = string(debug.Stack()) - } - }() - - // call the target function - f() +func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) { + didPanic = true + defer func() { + message = recover() + if didPanic { + stack = string(debug.Stack()) + } }() - return didPanic, message, stack + // call the target function + f() + didPanic = false + return } // Panics asserts that the code inside the specified PanicTestFunc panics. @@ -1161,11 +1159,15 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs bf, bok := toFloat(actual) if !aok || !bok { - return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) + return Fail(t, "Parameters must be numerical", msgAndArgs...) + } + + if math.IsNaN(af) && math.IsNaN(bf) { + return true } if math.IsNaN(af) { - return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...) + return Fail(t, "Expected must not be NaN", msgAndArgs...) } if math.IsNaN(bf) { @@ -1188,7 +1190,7 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { - return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + return Fail(t, "Parameters must be slice", msgAndArgs...) } actualSlice := reflect.ValueOf(actual) @@ -1250,8 +1252,12 @@ func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, m func calcRelativeError(expected, actual interface{}) (float64, error) { af, aok := toFloat(expected) - if !aok { - return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) + bf, bok := toFloat(actual) + if !aok || !bok { + return 0, fmt.Errorf("Parameters must be numerical") + } + if math.IsNaN(af) && math.IsNaN(bf) { + return 0, nil } if math.IsNaN(af) { return 0, errors.New("expected value must not be NaN") @@ -1259,10 +1265,6 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { if af == 0 { return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") } - bf, bok := toFloat(actual) - if !bok { - return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) - } if math.IsNaN(bf) { return 0, errors.New("actual value must not be NaN") } @@ -1298,7 +1300,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { - return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + return Fail(t, "Parameters must be slice", msgAndArgs...) } actualSlice := reflect.ValueOf(actual) @@ -1375,6 +1377,27 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte return true } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) +func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !Error(t, theError, msgAndArgs...) { + return false + } + + actual := theError.Error() + if !strings.Contains(actual, contains) { + return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...) + } + + return true +} + // matchRegexp return true if a specified regexp matches a string. func matchRegexp(rx interface{}, str interface{}) bool { @@ -1588,12 +1611,17 @@ func diff(expected interface{}, actual interface{}) string { } var e, a string - if et != reflect.TypeOf("") { - e = spewConfig.Sdump(expected) - a = spewConfig.Sdump(actual) - } else { + + switch et { + case reflect.TypeOf(""): e = reflect.ValueOf(expected).String() a = reflect.ValueOf(actual).String() + case reflect.TypeOf(time.Time{}): + e = spewConfigStringerEnabled.Sdump(expected) + a = spewConfigStringerEnabled.Sdump(actual) + default: + e = spewConfig.Sdump(expected) + a = spewConfig.Sdump(actual) } diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ @@ -1625,6 +1653,14 @@ var spewConfig = spew.ConfigState{ MaxDepth: 10, } +var spewConfigStringerEnabled = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, + MaxDepth: 10, +} + type tHelper interface { Helper() } diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go index e2e6a2d237df0..853da6cce2de9 100644 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -221,6 +221,14 @@ type Mock struct { mutex sync.Mutex } +// String provides a %v format string for Mock. +// Note: this is used implicitly by Arguments.Diff if a Mock is passed. +// It exists because go's default %v formatting traverses the struct +// without acquiring the mutex, which is detected by go test -race. +func (m *Mock) String() string { + return fmt.Sprintf("%[1]T<%[1]p>", m) +} + // TestData holds any data that might be useful for testing. Testify ignores // this data completely allowing you to do whatever you like with it. func (m *Mock) TestData() objx.Map { @@ -720,7 +728,7 @@ func (f argumentMatcher) Matches(argument interface{}) bool { } func (f argumentMatcher) String() string { - return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).Name()) + return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).String()) } // MatchedBy can be used to match a mock call based on only certain properties diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 51820df2e6726..59c48277ac6d3 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -280,6 +280,36 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int t.FailNow() } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) +func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContains(t, theError, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContainsf(t, theError, contains, msg, args...) { + return + } + t.FailNow() +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index ed54a9d83f35e..5bb07c89c68b7 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -223,6 +223,30 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. ErrorAsf(a.t, err, target, msg, args...) } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) +func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContainsf(a.t, theError, contains, msg, args...) +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { diff --git a/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/reader.go b/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/reader.go deleted file mode 100644 index 83df304e83aa6..0000000000000 --- a/vendor/gopkg.in/Graylog2/go-gelf.v2/gelf/reader.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2012 SocialCode. All rights reserved. -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. - -package gelf - -import ( - "bytes" - "compress/gzip" - "compress/zlib" - "encoding/json" - "fmt" - "io" - "net" - "strings" - "sync" -) - -type Reader struct { - mu sync.Mutex - conn net.Conn -} - -func NewReader(addr string) (*Reader, error) { - var err error - udpAddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return nil, fmt.Errorf("ResolveUDPAddr('%s'): %s", addr, err) - } - - conn, err := net.ListenUDP("udp", udpAddr) - if err != nil { - return nil, fmt.Errorf("ListenUDP: %s", err) - } - - r := new(Reader) - r.conn = conn - return r, nil -} - -func (r *Reader) Addr() string { - return r.conn.LocalAddr().String() -} - -// FIXME: this will discard data if p isn't big enough to hold the -// full message. -func (r *Reader) Read(p []byte) (int, error) { - msg, err := r.ReadMessage() - if err != nil { - return -1, err - } - - var data string - - if msg.Full == "" { - data = msg.Short - } else { - data = msg.Full - } - - return strings.NewReader(data).Read(p) -} - -func (r *Reader) ReadMessage() (*Message, error) { - cBuf := make([]byte, ChunkSize) - var ( - err error - n, length int - cid, ocid []byte - seq, total uint8 - cHead []byte - cReader io.Reader - chunks [][]byte - ) - - for got := 0; got < 128 && (total == 0 || got < int(total)); got++ { - if n, err = r.conn.Read(cBuf); err != nil { - return nil, fmt.Errorf("Read: %s", err) - } - cHead, cBuf = cBuf[:2], cBuf[:n] - - if bytes.Equal(cHead, magicChunked) { - // fmt.Printf("chunked %v\n", cBuf[:14]) - cid, seq, total = cBuf[2:2+8], cBuf[2+8], cBuf[2+8+1] - if ocid != nil && !bytes.Equal(cid, ocid) { - return nil, fmt.Errorf("out-of-band message %v (awaited %v)", cid, ocid) - } else if ocid == nil { - ocid = cid - chunks = make([][]byte, total) - } - n = len(cBuf) - chunkedHeaderLen - // fmt.Printf("setting chunks[%d]: %d\n", seq, n) - chunks[seq] = append(make([]byte, 0, n), cBuf[chunkedHeaderLen:]...) - length += n - } else { // not chunked - if total > 0 { - return nil, fmt.Errorf("out-of-band message (not chunked)") - } - break - } - } - // fmt.Printf("\nchunks: %v\n", chunks) - - if length > 0 { - if cap(cBuf) < length { - cBuf = append(cBuf, make([]byte, 0, length-cap(cBuf))...) - } - cBuf = cBuf[:0] - for i := range chunks { - // fmt.Printf("appending %d %v\n", i, chunks[i]) - cBuf = append(cBuf, chunks[i]...) - } - cHead = cBuf[:2] - } - - // the data we get from the wire is compressed - if bytes.Equal(cHead, magicGzip) { - cReader, err = gzip.NewReader(bytes.NewReader(cBuf)) - } else if cHead[0] == magicZlib[0] && - (int(cHead[0])*256+int(cHead[1]))%31 == 0 { - // zlib is slightly more complicated, but correct - cReader, err = zlib.NewReader(bytes.NewReader(cBuf)) - } else { - // compliance with https://github.com/Graylog2/graylog2-server - // treating all messages as uncompressed if they are not gzip, zlib or - // chunked - cReader = bytes.NewReader(cBuf) - } - - if err != nil { - return nil, fmt.Errorf("NewReader: %s", err) - } - - msg := new(Message) - if err := json.NewDecoder(cReader).Decode(&msg); err != nil { - return nil, fmt.Errorf("json.Unmarshal: %s", err) - } - - return msg, nil -} - -func (r *Reader) Close() error { - return r.conn.Close() -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 201df80004db4..90e2c6ab9364c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -546,6 +546,9 @@ github.com/grafana/dskit/runtimeconfig github.com/grafana/dskit/services github.com/grafana/dskit/spanlogger github.com/grafana/dskit/tenant +# github.com/grafana/go-gelf/v2 v2.0.1 +## explicit; go 1.17 +github.com/grafana/go-gelf/v2/gelf # github.com/grafana/regexp v0.0.0-20220304100321-149c8afcd6cb ## explicit; go 1.17 github.com/grafana/regexp @@ -967,7 +970,7 @@ github.com/spf13/pflag # github.com/stretchr/objx v0.2.0 ## explicit; go 1.12 github.com/stretchr/objx -# github.com/stretchr/testify v1.7.0 +# github.com/stretchr/testify v1.7.1 ## explicit; go 1.13 github.com/stretchr/testify/assert github.com/stretchr/testify/mock @@ -1396,9 +1399,6 @@ google.golang.org/protobuf/types/known/emptypb google.golang.org/protobuf/types/known/fieldmaskpb google.golang.org/protobuf/types/known/timestamppb google.golang.org/protobuf/types/known/wrapperspb -# gopkg.in/Graylog2/go-gelf.v2 v2.0.0-20191017102106-1550ee647df0 => github.com/grafana/go-gelf v0.0.0-20211112153804-126646b86de8 -## explicit -gopkg.in/Graylog2/go-gelf.v2/gelf # gopkg.in/alecthomas/kingpin.v2 v2.2.6 ## explicit gopkg.in/alecthomas/kingpin.v2 @@ -1660,5 +1660,4 @@ sigs.k8s.io/yaml # github.com/hashicorp/consul => github.com/hashicorp/consul v1.5.1 # github.com/gocql/gocql => github.com/grafana/gocql v0.0.0-20200605141915-ba5dc39ece85 # github.com/bradfitz/gomemcache => github.com/themihai/gomemcache v0.0.0-20180902122335-24332e2d58ab -# gopkg.in/Graylog2/go-gelf.v2 => github.com/grafana/go-gelf v0.0.0-20211112153804-126646b86de8 # github.com/cloudflare/cloudflare-go => github.com/cyriltovena/cloudflare-go v0.27.1-0.20211118103540-ff77400bcb93