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,93 @@
# Example Input Plugin
The `example` plugin gathers metrics about example things. This description
explains at a high level what the plugin does and provides links to where
additional information can be found.
Telegraf minimum version: Telegraf x.x Plugin minimum tested version: x.x
⭐ Telegraf v1.0.0 <!-- introduction version -->
🚩 Telegraf v1.10.0 <!-- deprecation version if any -->
🔥 Telegraf v1.20.0 <!-- removal version if any -->
🏷️ system <!-- plugin tags -->
💻 all <!-- OS support -->
## 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
# This is an example plugin
[[inputs.example]]
example_option = "example_value"
```
Running `telegraf --usage <plugin-name>` also gives the sample TOML
configuration.
### example_option
A more in depth description of an option can be provided here, but only do so if
the option cannot be fully described in the sample config.
## Metrics
Here you should add an optional description and links to where the user can get
more information about the measurements.
If the output is determined dynamically based on the input source, or there are
more metrics than can reasonably be listed, describe how the input is mapped to
the output.
- measurement1
- tags:
- tag1 (optional description)
- tag2
- fields:
- field1 (type, unit)
- field2 (float, percent)
- measurement2
- tags:
- tag3
- fields:
- field3 (integer, bytes)
- field4 (integer, green=1 yellow=2 red=3)
- field5 (string)
- field6 (float)
- field7 (boolean)
## Sample Queries
This section can contain some useful InfluxDB queries that can be used to get
started with the plugin or to generate dashboards. For each query listed,
describe at a high level what data is returned.
Get the max, mean, and min for the measurement in the last hour:
```sql
SELECT max(field1), mean(field1), min(field1) FROM measurement1 WHERE tag1=bar AND time > now() - 1h GROUP BY tag
```
## Troubleshooting
This optional section can provide basic troubleshooting steps that a user can
perform.
## Example Output
This section shows example output in Line Protocol format. You can often use
`telegraf --input-filter <plugin-name> --test` or use the `file` output to get
this information.
```text
measurement1,tag1=foo,tag2=bar field1=1i,field2=2.1 1453831884664956455
measurement2,tag1=foo,tag2=bar,tag3=baz field3=1i 1453831884664956455
```

View file

@ -0,0 +1,139 @@
//go:generate ../../../tools/readme_config_includer/generator
package example
import (
"crypto/rand"
_ "embed"
"errors"
"fmt"
"math"
"math/big"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
// Example struct should be named the same as the Plugin
type Example struct {
// Example for a mandatory option to set a tag
DeviceName string `toml:"device_name"`
// Config options are converted to the correct type automatically
NumberFields int64 `toml:"number_fields"`
// We can also use booleans and have diverging names between user-configuration options and struct members
EnableRandomVariable bool `toml:"enable_random"`
// Example of passing a duration option allowing the format of e.g. "100ms", "5m" or "1h"
Timeout config.Duration `toml:"timeout"`
// Example of passing a password/token/username or other sensitive data with the secret-store
UserName config.Secret `toml:"username"`
Password config.Secret `toml:"password"`
// Telegraf logging facility
// The exact name is important to allow automatic initialization by telegraf.
Log telegraf.Logger `toml:"-"`
// This is a non-exported internal state.
count int64
}
func (*Example) SampleConfig() string {
return sampleConfig
}
// Init can be implemented to do one-time processing stuff like initializing variables
func (m *Example) Init() error {
// Check your options according to your requirements
if m.DeviceName == "" {
return errors.New("device name cannot be empty")
}
// Set your defaults.
// Please note: In golang all fields are initialized to their nil value, so you should not
// set these fields if the nil value is what you want (e.g. for booleans).
if m.NumberFields < 1 {
m.Log.Debugf("Setting number of fields to default from invalid value %d", m.NumberFields)
m.NumberFields = 2
}
// Check using the secret-store
if m.UserName.Empty() {
// For example, use a default value
m.Log.Debug("using default username")
}
// Retrieve credentials using the secret-store
password, err := m.Password.Get()
if err != nil {
return fmt.Errorf("getting password failed: %w", err)
}
defer password.Destroy()
// Initialize your internal states
m.count = 1
return nil
}
// Gather defines what data the plugin will gather.
func (m *Example) Gather(acc telegraf.Accumulator) error {
// Imagine some completely arbitrary error occurring here
if m.NumberFields > 10 {
return errors.New("too many fields")
}
// For illustration, we gather three metrics in one go
for run := 0; run < 3; run++ {
// Imagine an error occurs here, but you want to keep the other
// metrics, then you cannot simply return, as this would drop
// all later metrics. Simply accumulate errors in this case
// and ignore the metric.
if m.EnableRandomVariable && m.DeviceName == "flappy" && run > 1 {
acc.AddError(errors.New("too many runs for random values"))
continue
}
// Construct the fields
fields := map[string]interface{}{"count": m.count}
for i := int64(1); i < m.NumberFields; i++ {
name := fmt.Sprintf("field%d", i)
var err error
value := big.NewInt(0)
if m.EnableRandomVariable {
value, err = rand.Int(rand.Reader, big.NewInt(math.MaxUint32))
if err != nil {
acc.AddError(err)
continue
}
}
fields[name] = float64(value.Int64())
}
// Construct the tags
tags := map[string]string{"device": m.DeviceName}
// Add the metric with the current timestamp
acc.AddFields("example", fields, tags)
m.count++
}
return nil
}
// Register the plugin
func init() {
inputs.Add("example", func() telegraf.Input {
return &Example{
// Set the default timeout here to distinguish it from the user setting it to zero
Timeout: config.Duration(100 * time.Millisecond),
}
})
}

View file

@ -0,0 +1,439 @@
package example
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/testutil"
)
// This file should contain a set of unit-tests to cover your plugin. This will ease
// spotting bugs and mistakes when later modifying or extending the functionality.
// To do so, please write one 'TestXYZ' function per 'case' e.g. default init,
// things that should fail or expected values from a mockup.
func TestInitDefault(t *testing.T) {
// This test should succeed with the default initialization.
// Use whatever you use in the init() function plus the mandatory options.
// ATTENTION: Always initialize the "Log" as you will get SIGSEGV otherwise.
plugin := &Example{
DeviceName: "test",
Timeout: config.Duration(100 * time.Millisecond),
Log: testutil.Logger{},
}
// Test the initialization succeeds
require.NoError(t, plugin.Init())
// Also test that default values are set correctly
require.Equal(t, config.Duration(100*time.Millisecond), plugin.Timeout)
require.Equal(t, "test", plugin.DeviceName)
require.Equal(t, int64(2), plugin.NumberFields)
}
func TestInitFail(t *testing.T) {
// You should also test for your safety nets to work i.e. you get errors for
// invalid configuration-option values. So check your error paths in Init()
// and check if you reach them
// We setup a table-test here to specify "setting" - "expected error" values.
// Even though it seems overkill here for the example plugin, we reuse this structure
// later for checking the metrics
tests := []struct {
name string
plugin *Example
expected string
}{
{
name: "all empty",
plugin: &Example{},
expected: "device name cannot be empty",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Always initialize the logger to avoid SIGSEGV. This is done automatically by
// telegraf during normal operation.
tt.plugin.Log = testutil.Logger{}
err := tt.plugin.Init()
require.Error(t, err)
require.EqualError(t, err, tt.expected)
})
}
}
func TestFixedValue(t *testing.T) {
// You can organize the test e.g. by operation mode (like we do here random vs. fixed), by features or
// by different metrics gathered. Please choose the partitioning most suited for your plugin
// We again setup a table-test here to specify "setting" - "expected output metric" pairs.
tests := []struct {
name string
plugin *Example
expected []telegraf.Metric
}{
{
name: "count only",
plugin: &Example{
DeviceName: "test",
NumberFields: 1,
},
expected: []telegraf.Metric{
testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 1,
},
time.Unix(0, 0),
),
testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 2,
},
time.Unix(0, 0),
),
testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 3,
},
time.Unix(0, 0),
),
},
},
{
name: "default settings",
plugin: &Example{
DeviceName: "test",
},
expected: []telegraf.Metric{
testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 1,
"field1": float64(0),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 2,
"field1": float64(0),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 3,
"field1": float64(0),
},
time.Unix(0, 0),
),
},
},
{
name: "more fields",
plugin: &Example{
DeviceName: "test",
NumberFields: 4,
},
expected: []telegraf.Metric{
testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 1,
"field1": float64(0),
"field2": float64(0),
"field3": float64(0),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 2,
"field1": float64(0),
"field2": float64(0),
"field3": float64(0),
},
time.Unix(0, 0),
),
testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 3,
"field1": float64(0),
"field2": float64(0),
"field3": float64(0),
},
time.Unix(0, 0),
),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var acc testutil.Accumulator
tt.plugin.Log = testutil.Logger{}
require.NoError(t, tt.plugin.Init())
// Call gather and check no error occurs. In case you use acc.AddError() somewhere
// in your code, it is not sufficient to only check the return value of Gather().
require.NoError(t, tt.plugin.Gather(&acc))
require.Empty(t, acc.Errors, "found errors accumulated by acc.AddError()")
// Wait for the expected number of metrics to avoid flaky tests due to
// race conditions.
acc.Wait(len(tt.expected))
// Compare the metrics in a convenient way. Here we ignore
// the metric time during comparison as we cannot inject the time
// during test. For more comparison options check testutil package.
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
})
}
}
func TestRandomValue(t *testing.T) {
// Sometimes, you cannot know the exact outcome of the gather cycle e.g. if the gathering involves random data.
// However, you should check the result nevertheless, applying as many conditions as you can.
// We again setup a table-test here to specify "setting" - "expected output metric" pairs.
tests := []struct {
name string
plugin *Example
template telegraf.Metric
}{
{
name: "count only",
plugin: &Example{
DeviceName: "test",
NumberFields: 1,
EnableRandomVariable: true,
},
template: testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 1,
},
time.Unix(0, 0),
),
},
{
name: "default settings",
plugin: &Example{
DeviceName: "test",
EnableRandomVariable: true,
},
template: testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 1,
"field1": float64(0),
},
time.Unix(0, 0),
),
},
{
name: "more fields",
plugin: &Example{
DeviceName: "test",
NumberFields: 4,
EnableRandomVariable: true,
},
template: testutil.MustMetric(
"example",
map[string]string{
"device": "test",
},
map[string]interface{}{
"count": 1,
"field1": float64(0),
"field2": float64(0),
"field3": float64(0),
},
time.Unix(0, 0),
),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var acc testutil.Accumulator
tt.plugin.Log = testutil.Logger{}
require.NoError(t, tt.plugin.Init())
// Call gather and check no error occurs. In case you use acc.AddError() somewhere
// in your code, it is not sufficient to only check the return value of Gather().
require.NoError(t, tt.plugin.Gather(&acc))
require.Empty(t, acc.Errors, "found errors accumulated by acc.AddError()")
// Wait for the expected number of metrics to avoid flaky tests due to
// race conditions.
acc.Wait(3)
// Compare all aspects of the metric that are known to you
for i, m := range acc.GetTelegrafMetrics() {
require.Equal(t, m.Name(), tt.template.Name())
require.Equal(t, m.Tags(), tt.template.Tags())
// Check if all expected fields are there
fields := m.Fields()
for k := range tt.template.Fields() {
if k == "count" {
require.Equal(t, fields["count"], int64(i+1))
continue
}
_, found := fields[k]
require.Truef(t, found, "field %q not found", k)
}
}
})
}
}
func TestGatherFail(t *testing.T) {
// You should also test for error conditions in your Gather() method. Try to cover all error paths.
// We again setup a table-test here to specify "setting" - "expected error" pair.
tests := []struct {
name string
plugin *Example
expected string
}{
{
name: "too many fields",
plugin: &Example{
DeviceName: "test",
NumberFields: 11,
},
expected: "too many fields",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var acc testutil.Accumulator
tt.plugin.Log = testutil.Logger{}
require.NoError(t, tt.plugin.Init())
err := tt.plugin.Gather(&acc)
require.Error(t, err)
require.EqualError(t, err, tt.expected)
})
}
}
func TestRandomValueFailPartial(t *testing.T) {
// You should also test for error conditions in your Gather() with partial output. This is required when
// using acc.AddError() as Gather() might succeed (return nil) but there are some metrics missing.
// We again setup a table-test here to specify "setting" - "expected output metric" and "errors".
tests := []struct {
name string
plugin *Example
expected []telegraf.Metric
expectedErr string
}{
{
name: "flappy gather",
plugin: &Example{
DeviceName: "flappy",
NumberFields: 1,
EnableRandomVariable: true,
},
expected: []telegraf.Metric{
testutil.MustMetric(
"example",
map[string]string{
"device": "flappy",
},
map[string]interface{}{
"count": 1,
},
time.Unix(0, 0),
),
testutil.MustMetric(
"example",
map[string]string{
"device": "flappy",
},
map[string]interface{}{
"count": 2,
},
time.Unix(0, 0),
),
},
expectedErr: "too many runs for random values",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var acc testutil.Accumulator
tt.plugin.Log = testutil.Logger{}
require.NoError(t, tt.plugin.Init())
// Call gather and check no error occurs. However, we expect an error accumulated by acc.AddError()
require.NoError(t, tt.plugin.Gather(&acc))
// Wait for the expected number of metrics to avoid flaky tests due to
// race conditions.
acc.Wait(len(tt.expected))
// Check the accumulated errors
require.Len(t, acc.Errors, 1)
require.EqualError(t, acc.Errors[0], tt.expectedErr)
// Compare the expected partial metrics.
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
})
}
}

View file

@ -0,0 +1,3 @@
# This is an example plugin
[[inputs.example]]
example_option = "example_value"