1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,62 @@
# Socket Writer Output Plugin
This plugin writes metrics to a network service e.g. via UDP or TCP in one of
the supported [data formats][data_formats].
⭐ Telegraf v1.3.0
🏷️ applications, network
💻 all
[data_formats]: /docs/DATA_FORMATS_OUTPUT.md
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Configuration
```toml @sample.conf
# Generic socket writer capable of handling multiple socket types.
[[outputs.socket_writer]]
## URL to connect to
# address = "tcp://127.0.0.1:8094"
# address = "tcp://example.com:http"
# address = "tcp4://127.0.0.1:8094"
# address = "tcp6://127.0.0.1:8094"
# address = "tcp6://[2001:db8::1]:8094"
# address = "udp://127.0.0.1:8094"
# address = "udp4://127.0.0.1:8094"
# address = "udp6://127.0.0.1:8094"
# address = "unix:///tmp/telegraf.sock"
# address = "unixgram:///tmp/telegraf.sock"
# address = "vsock://cid:port"
## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false
## Period between keep alive probes.
## Only applies to TCP sockets.
## 0 disables keep alive probes.
## Defaults to the OS configuration.
# keep_alive_period = "5m"
## Content encoding for message payloads, can be set to "gzip" or to
## "identity" to apply no encoding.
##
# content_encoding = "identity"
## Data format to generate.
## Each data format has its own unique set of configuration options, read
## more about them here:
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
# data_format = "influx"
```

View file

@ -0,0 +1,38 @@
# Generic socket writer capable of handling multiple socket types.
[[outputs.socket_writer]]
## URL to connect to
# address = "tcp://127.0.0.1:8094"
# address = "tcp://example.com:http"
# address = "tcp4://127.0.0.1:8094"
# address = "tcp6://127.0.0.1:8094"
# address = "tcp6://[2001:db8::1]:8094"
# address = "udp://127.0.0.1:8094"
# address = "udp4://127.0.0.1:8094"
# address = "udp6://127.0.0.1:8094"
# address = "unix:///tmp/telegraf.sock"
# address = "unixgram:///tmp/telegraf.sock"
# address = "vsock://cid:port"
## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false
## Period between keep alive probes.
## Only applies to TCP sockets.
## 0 disables keep alive probes.
## Defaults to the OS configuration.
# keep_alive_period = "5m"
## Content encoding for message payloads, can be set to "gzip" or to
## "identity" to apply no encoding.
##
# content_encoding = "identity"
## Data format to generate.
## Each data format has its own unique set of configuration options, read
## more about them here:
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
# data_format = "influx"

View file

@ -0,0 +1,185 @@
//go:generate ../../../tools/readme_config_includer/generator
package socket_writer
import (
"crypto/tls"
_ "embed"
"errors"
"fmt"
"math"
"net"
"strconv"
"strings"
"time"
"github.com/mdlayher/vsock"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/internal"
common_tls "github.com/influxdata/telegraf/plugins/common/tls"
"github.com/influxdata/telegraf/plugins/outputs"
)
//go:embed sample.conf
var sampleConfig string
type SocketWriter struct {
ContentEncoding string `toml:"content_encoding"`
Address string
KeepAlivePeriod *config.Duration
common_tls.ClientConfig
Log telegraf.Logger `toml:"-"`
serializer telegraf.Serializer
encoder internal.ContentEncoder
net.Conn
}
func (*SocketWriter) SampleConfig() string {
return sampleConfig
}
func (sw *SocketWriter) SetSerializer(s telegraf.Serializer) {
sw.serializer = s
}
func (sw *SocketWriter) Connect() error {
spl := strings.SplitN(sw.Address, "://", 2)
if len(spl) != 2 {
return fmt.Errorf("invalid address: %s", sw.Address)
}
tlsCfg, err := sw.ClientConfig.TLSConfig()
if err != nil {
return err
}
var c net.Conn
if spl[0] == "vsock" {
addrTuple := strings.SplitN(spl[1], ":", 2)
// Check address string for containing two
if len(addrTuple) < 2 {
return errors.New("port and/or CID number missing")
}
// Parse CID and port number from address string both being 32-bit
// source: https://man7.org/linux/man-pages/man7/vsock.7.html
cid, err := strconv.ParseUint(addrTuple[0], 10, 32)
if err != nil {
return fmt.Errorf("failed to parse CID %s: %w", addrTuple[0], err)
}
if (cid >= uint64(math.Pow(2, 32))-1) && (cid <= 0) {
return fmt.Errorf("value of CID %d is out of range", cid)
}
port, err := strconv.ParseUint(addrTuple[1], 10, 32)
if err != nil {
return fmt.Errorf("failed to parse port number %s: %w", addrTuple[1], err)
}
if (port >= uint64(math.Pow(2, 32))-1) && (port <= 0) {
return fmt.Errorf("port number %d is out of range", port)
}
c, err = vsock.Dial(uint32(cid), uint32(port), nil)
if err != nil {
return err
}
} else {
if tlsCfg == nil {
c, err = net.Dial(spl[0], spl[1])
} else {
c, err = tls.Dial(spl[0], spl[1], tlsCfg)
}
if err != nil {
return err
}
}
if err := sw.setKeepAlive(c); err != nil {
sw.Log.Debugf("Unable to configure keep alive (%s): %s", sw.Address, err)
}
// set encoder
sw.encoder, err = internal.NewContentEncoder(sw.ContentEncoding)
if err != nil {
return err
}
sw.Conn = c
return nil
}
func (sw *SocketWriter) setKeepAlive(c net.Conn) error {
if sw.KeepAlivePeriod == nil {
return nil
}
tcpc, ok := c.(*net.TCPConn)
if !ok {
return fmt.Errorf("cannot set keep alive on a %s socket", strings.SplitN(sw.Address, "://", 2)[0])
}
if *sw.KeepAlivePeriod == 0 {
return tcpc.SetKeepAlive(false)
}
if err := tcpc.SetKeepAlive(true); err != nil {
return err
}
return tcpc.SetKeepAlivePeriod(time.Duration(*sw.KeepAlivePeriod))
}
// Write writes the given metrics to the destination.
// If an error is encountered, it is up to the caller to retry the same write again later.
// Not parallel safe.
func (sw *SocketWriter) Write(metrics []telegraf.Metric) error {
if sw.Conn == nil {
// previous write failed with permanent error and socket was closed.
if err := sw.Connect(); err != nil {
return err
}
}
for _, m := range metrics {
bs, err := sw.serializer.Serialize(m)
if err != nil {
sw.Log.Debugf("Could not serialize metric: %v", err)
continue
}
bs, err = sw.encoder.Encode(bs)
if err != nil {
sw.Log.Debugf("Could not encode metric: %v", err)
continue
}
if _, err := sw.Conn.Write(bs); err != nil {
// TODO log & keep going with remaining strings
var netErr net.Error
if errors.As(err, &netErr) {
// permanent error. close the connection
sw.Close()
sw.Conn = nil
return fmt.Errorf("closing connection: %w", netErr)
}
return err
}
}
return nil
}
// Close closes the connection. Noop if already closed.
func (sw *SocketWriter) Close() error {
if sw.Conn == nil {
return nil
}
err := sw.Conn.Close()
sw.Conn = nil
return err
}
func init() {
outputs.Add("socket_writer", func() telegraf.Output {
return &SocketWriter{}
})
}

View file

@ -0,0 +1,211 @@
package socket_writer
import (
"bufio"
"net"
"runtime"
"sync"
"testing"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/serializers/influx"
"github.com/influxdata/telegraf/testutil"
)
func newSocketWriter(t *testing.T, addr string) *SocketWriter {
serializer := &influx.Serializer{}
require.NoError(t, serializer.Init())
return &SocketWriter{
Address: addr,
serializer: serializer,
}
}
func TestSocketWriter_tcp(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
sw := newSocketWriter(t, "tcp://"+listener.Addr().String())
require.NoError(t, sw.Connect())
lconn, err := listener.Accept()
require.NoError(t, err)
testSocketWriterStream(t, sw, lconn)
}
func TestSocketWriter_udp(t *testing.T) {
listener, err := net.ListenPacket("udp", "127.0.0.1:0")
require.NoError(t, err)
sw := newSocketWriter(t, "udp://"+listener.LocalAddr().String())
require.NoError(t, sw.Connect())
testSocketWriterPacket(t, sw, listener)
}
func TestSocketWriter_unix(t *testing.T) {
sock := testutil.TempSocket(t)
listener, err := net.Listen("unix", sock)
require.NoError(t, err)
sw := newSocketWriter(t, "unix://"+sock)
require.NoError(t, sw.Connect())
lconn, err := listener.Accept()
require.NoError(t, err)
testSocketWriterStream(t, sw, lconn)
}
func TestSocketWriter_unixgram(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping on Windows, as unixgram sockets are not supported")
}
sock := testutil.TempSocket(t)
listener, err := net.ListenPacket("unixgram", sock)
require.NoError(t, err)
sw := newSocketWriter(t, "unixgram://"+sock)
require.NoError(t, sw.Connect())
testSocketWriterPacket(t, sw, listener)
}
func testSocketWriterStream(t *testing.T, sw *SocketWriter, lconn net.Conn) {
metrics := []telegraf.Metric{testutil.TestMetric(1, "test")}
mbs1out, err := sw.serializer.Serialize(metrics[0])
require.NoError(t, err)
mbs1out, err = sw.encoder.Encode(mbs1out)
require.NoError(t, err)
metrics = append(metrics, testutil.TestMetric(2, "test"))
mbs2out, err := sw.serializer.Serialize(metrics[1])
require.NoError(t, err)
mbs2out, err = sw.encoder.Encode(mbs2out)
require.NoError(t, err)
err = sw.Write(metrics)
require.NoError(t, err)
scnr := bufio.NewScanner(lconn)
require.True(t, scnr.Scan())
mstr1in := scnr.Text() + "\n"
require.True(t, scnr.Scan())
mstr2in := scnr.Text() + "\n"
require.Equal(t, string(mbs1out), mstr1in)
require.Equal(t, string(mbs2out), mstr2in)
}
func testSocketWriterPacket(t *testing.T, sw *SocketWriter, lconn net.PacketConn) {
metrics := []telegraf.Metric{testutil.TestMetric(1, "test")}
mbs1out, err := sw.serializer.Serialize(metrics[0])
require.NoError(t, err)
mbs1out, err = sw.encoder.Encode(mbs1out)
require.NoError(t, err)
mbs1str := string(mbs1out)
metrics = append(metrics, testutil.TestMetric(2, "test"))
mbs2out, err := sw.serializer.Serialize(metrics[1])
require.NoError(t, err)
mbs2out, err = sw.encoder.Encode(mbs2out)
require.NoError(t, err)
mbs2str := string(mbs2out)
err = sw.Write(metrics)
require.NoError(t, err)
buf := make([]byte, 256)
var mstrins []string
for len(mstrins) < 2 {
n, _, err := lconn.ReadFrom(buf)
require.NoError(t, err)
mstrins = append(mstrins, string(buf[:n]))
}
require.Len(t, mstrins, 2)
require.Equal(t, mbs1str, mstrins[0])
require.Equal(t, mbs2str, mstrins[1])
}
func TestSocketWriter_Write_err(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
sw := newSocketWriter(t, "tcp://"+listener.Addr().String())
require.NoError(t, sw.Connect())
require.NoError(t, sw.Conn.(*net.TCPConn).SetReadBuffer(256))
lconn, err := listener.Accept()
require.NoError(t, err)
err = lconn.(*net.TCPConn).SetWriteBuffer(256)
require.NoError(t, err)
metrics := []telegraf.Metric{testutil.TestMetric(1, "testerr")}
// close the socket to generate an error
err = lconn.Close()
require.NoError(t, err)
err = sw.Conn.Close()
require.NoError(t, err)
err = sw.Write(metrics)
require.Error(t, err)
require.Nil(t, sw.Conn)
}
func TestSocketWriter_Write_reconnect(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
sw := newSocketWriter(t, "tcp://"+listener.Addr().String())
require.NoError(t, sw.Connect())
require.NoError(t, sw.Conn.(*net.TCPConn).SetReadBuffer(256))
lconn, err := listener.Accept()
require.NoError(t, err)
err = lconn.(*net.TCPConn).SetWriteBuffer(256)
require.NoError(t, err)
err = lconn.Close()
require.NoError(t, err)
sw.Conn = nil
wg := sync.WaitGroup{}
wg.Add(1)
var lerr error
go func() {
lconn, lerr = listener.Accept()
wg.Done()
}()
metrics := []telegraf.Metric{testutil.TestMetric(1, "testerr")}
err = sw.Write(metrics)
require.NoError(t, err)
wg.Wait()
require.NoError(t, lerr)
mbsout, err := sw.serializer.Serialize(metrics[0])
require.NoError(t, err)
buf := make([]byte, 256)
n, err := lconn.Read(buf)
require.NoError(t, err)
require.Equal(t, string(mbsout), string(buf[:n]))
}
func TestSocketWriter_udp_gzip(t *testing.T) {
listener, err := net.ListenPacket("udp", "127.0.0.1:0")
require.NoError(t, err)
sw := newSocketWriter(t, "udp://"+listener.LocalAddr().String())
sw.ContentEncoding = "gzip"
require.NoError(t, sw.Connect())
testSocketWriterPacket(t, sw, listener)
}