Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
70
plugins/outputs/mongodb/README.md
Normal file
70
plugins/outputs/mongodb/README.md
Normal file
|
@ -0,0 +1,70 @@
|
|||
# MongoDB Output Plugin
|
||||
|
||||
This plugin writes metrics to [MongoDB][mongodb] automatically creating
|
||||
collections as time series collections if they don't exist.
|
||||
|
||||
> [!NOTE]
|
||||
> This plugin requires MongoDB v5 or later for time series collections.
|
||||
|
||||
⭐ Telegraf v1.21.0
|
||||
🏷️ datastore
|
||||
💻 all
|
||||
|
||||
[mongodb]: https://www.mongodb.com
|
||||
|
||||
## 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
|
||||
|
||||
## Secret-store support
|
||||
|
||||
This plugin supports secrets from secret-stores for the `username` and
|
||||
`password` option.
|
||||
See the [secret-store documentation][SECRETSTORE] for more details on how
|
||||
to use them.
|
||||
|
||||
[SECRETSTORE]: ../../../docs/CONFIGURATION.md#secret-store-secrets
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# A plugin that can transmit logs to mongodb
|
||||
[[outputs.mongodb]]
|
||||
# connection string examples for mongodb
|
||||
dsn = "mongodb://localhost:27017"
|
||||
# dsn = "mongodb://mongod1:27017,mongod2:27017,mongod3:27017/admin&replicaSet=myReplSet&w=1"
|
||||
|
||||
# overrides serverSelectionTimeoutMS in dsn if set
|
||||
# timeout = "30s"
|
||||
|
||||
# default authentication, optional
|
||||
# authentication = "NONE"
|
||||
|
||||
# for SCRAM-SHA-256 authentication
|
||||
# authentication = "SCRAM"
|
||||
# username = "root"
|
||||
# password = "***"
|
||||
|
||||
# for x509 certificate authentication
|
||||
# authentication = "X509"
|
||||
# tls_ca = "ca.pem"
|
||||
# tls_key = "client.pem"
|
||||
# # tls_key_pwd = "changeme" # required for encrypted tls_key
|
||||
# insecure_skip_verify = false
|
||||
|
||||
# database to store measurements and time series collections
|
||||
# database = "telegraf"
|
||||
|
||||
# granularity can be seconds, minutes, or hours.
|
||||
# configuring this value will be based on your input collection frequency.
|
||||
# see https://docs.mongodb.com/manual/core/timeseries-collections/#create-a-time-series-collection
|
||||
# granularity = "seconds"
|
||||
|
||||
# optionally set a TTL to automatically expire documents from the measurement collections.
|
||||
# ttl = "360h"
|
||||
```
|
233
plugins/outputs/mongodb/mongodb.go
Normal file
233
plugins/outputs/mongodb/mongodb.go
Normal file
|
@ -0,0 +1,233 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package mongodb
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/common/tls"
|
||||
"github.com/influxdata/telegraf/plugins/outputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
func (s *MongoDB) getCollections(ctx context.Context) error {
|
||||
collections, err := s.client.Database(s.MetricDatabase).ListCollections(ctx, bson.M{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to execute ListCollections: %w", err)
|
||||
}
|
||||
s.collections = make(map[string]bson.M, collections.RemainingBatchLength())
|
||||
for collections.Next(ctx) {
|
||||
var collection bson.M
|
||||
if err = collections.Decode(&collection); err != nil {
|
||||
return fmt.Errorf("unable to decode ListCollections: %w", err)
|
||||
}
|
||||
name, ok := collection["name"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("non-string name in %v", collection)
|
||||
}
|
||||
s.collections[name] = collection
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MongoDB) insertDocument(ctx context.Context, databaseCollection string, bdoc bson.D) error {
|
||||
collection := s.client.Database(s.MetricDatabase).Collection(databaseCollection)
|
||||
_, err := collection.InsertOne(ctx, &bdoc)
|
||||
return err
|
||||
}
|
||||
|
||||
type MongoDB struct {
|
||||
Dsn string `toml:"dsn"`
|
||||
AuthenticationType string `toml:"authentication"`
|
||||
MetricDatabase string `toml:"database"`
|
||||
MetricGranularity string `toml:"granularity"`
|
||||
Username config.Secret `toml:"username"`
|
||||
Password config.Secret `toml:"password"`
|
||||
ServerSelectTimeout config.Duration `toml:"timeout"`
|
||||
TTL config.Duration `toml:"ttl"`
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
client *mongo.Client
|
||||
clientOptions *options.ClientOptions
|
||||
collections map[string]bson.M
|
||||
tls.ClientConfig
|
||||
}
|
||||
|
||||
func (*MongoDB) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (s *MongoDB) Init() error {
|
||||
if s.MetricDatabase == "" {
|
||||
s.MetricDatabase = "telegraf"
|
||||
}
|
||||
switch s.MetricGranularity {
|
||||
case "":
|
||||
s.MetricGranularity = "seconds"
|
||||
case "seconds", "minutes", "hours":
|
||||
default:
|
||||
return errors.New("invalid time series collection granularity. please specify \"seconds\", \"minutes\", or \"hours\"")
|
||||
}
|
||||
|
||||
// do some basic Dsn checks
|
||||
if !strings.HasPrefix(s.Dsn, "mongodb://") && !strings.HasPrefix(s.Dsn, "mongodb+srv://") {
|
||||
return errors.New("invalid connection string. expected mongodb://host:port/?{options} or mongodb+srv://host:port/?{options}")
|
||||
}
|
||||
if !strings.Contains(s.Dsn[strings.Index(s.Dsn, "://")+3:], "/") { // append '/' to Dsn if its missing
|
||||
s.Dsn = s.Dsn + "/"
|
||||
}
|
||||
|
||||
serverAPIOptions := options.ServerAPI(options.ServerAPIVersion1) // use new mongodb versioned api
|
||||
s.clientOptions = options.Client().SetServerAPIOptions(serverAPIOptions)
|
||||
|
||||
switch s.AuthenticationType {
|
||||
case "SCRAM":
|
||||
if s.Username.Empty() {
|
||||
return errors.New("authentication for SCRAM must specify a username")
|
||||
}
|
||||
if s.Password.Empty() {
|
||||
return errors.New("authentication for SCRAM must specify a password")
|
||||
}
|
||||
username, err := s.Username.Get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting username failed: %w", err)
|
||||
}
|
||||
password, err := s.Password.Get()
|
||||
if err != nil {
|
||||
username.Destroy()
|
||||
return fmt.Errorf("getting password failed: %w", err)
|
||||
}
|
||||
credential := options.Credential{
|
||||
AuthMechanism: "SCRAM-SHA-256",
|
||||
Username: username.String(),
|
||||
Password: password.String(),
|
||||
}
|
||||
username.Destroy()
|
||||
password.Destroy()
|
||||
s.clientOptions.SetAuth(credential)
|
||||
case "X509":
|
||||
// format connection string to include tls/x509 options
|
||||
newConnectionString, err := url.Parse(s.Dsn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q := newConnectionString.Query()
|
||||
q.Set("tls", "true")
|
||||
if s.InsecureSkipVerify {
|
||||
q.Set("tlsInsecure", strconv.FormatBool(s.InsecureSkipVerify))
|
||||
}
|
||||
if s.TLSCA != "" {
|
||||
q.Set("tlsCAFile", s.TLSCA)
|
||||
}
|
||||
q.Set("sslClientCertificateKeyFile", s.TLSKey)
|
||||
if s.TLSKeyPwd != "" {
|
||||
q.Set("sslClientCertificateKeyPassword", s.TLSKeyPwd)
|
||||
}
|
||||
newConnectionString.RawQuery = q.Encode()
|
||||
s.Dsn = newConnectionString.String()
|
||||
// always auth source $external
|
||||
credential := options.Credential{
|
||||
AuthSource: "$external",
|
||||
AuthMechanism: "MONGODB-X509",
|
||||
}
|
||||
s.clientOptions.SetAuth(credential)
|
||||
}
|
||||
|
||||
if s.ServerSelectTimeout != 0 {
|
||||
s.clientOptions.SetServerSelectionTimeout(time.Duration(s.ServerSelectTimeout))
|
||||
}
|
||||
|
||||
s.clientOptions.ApplyURI(s.Dsn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MongoDB) createTimeSeriesCollection(databaseCollection string) error {
|
||||
_, collectionExists := s.collections[databaseCollection]
|
||||
if !collectionExists {
|
||||
ctx := context.Background()
|
||||
tso := options.TimeSeries()
|
||||
tso.SetTimeField("timestamp")
|
||||
tso.SetMetaField("tags")
|
||||
tso.SetGranularity(s.MetricGranularity)
|
||||
cco := options.CreateCollection()
|
||||
if s.TTL != 0 {
|
||||
cco.SetExpireAfterSeconds(int64(time.Duration(s.TTL).Seconds()))
|
||||
}
|
||||
cco.SetTimeSeriesOptions(tso)
|
||||
err := s.client.Database(s.MetricDatabase).CreateCollection(ctx, databaseCollection, cco)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create time series collection: %w", err)
|
||||
}
|
||||
s.collections[databaseCollection] = bson.M{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MongoDB) Connect() error {
|
||||
ctx := context.Background()
|
||||
client, err := mongo.Connect(ctx, s.clientOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to connect: %w", err)
|
||||
}
|
||||
s.client = client
|
||||
if err = s.getCollections(ctx); err != nil {
|
||||
return fmt.Errorf("unable to get collections from specified metric database: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MongoDB) Close() error {
|
||||
ctx := context.Background()
|
||||
return s.client.Disconnect(ctx)
|
||||
}
|
||||
|
||||
// all metric/measurement fields are parent level of document
|
||||
// metadata field is named "tags"
|
||||
// mongodb stores timestamp as UTC. conversion should be performed during reads in app or in aggregation pipeline
|
||||
func marshalMetric(metric telegraf.Metric) bson.D {
|
||||
var bdoc bson.D
|
||||
for k, v := range metric.Fields() {
|
||||
bdoc = append(bdoc, primitive.E{Key: k, Value: v})
|
||||
}
|
||||
var tags bson.D
|
||||
for k, v := range metric.Tags() {
|
||||
tags = append(tags, primitive.E{Key: k, Value: v})
|
||||
}
|
||||
bdoc = append(bdoc,
|
||||
primitive.E{Key: "tags", Value: tags},
|
||||
primitive.E{Key: "timestamp", Value: metric.Time()},
|
||||
)
|
||||
return bdoc
|
||||
}
|
||||
|
||||
func (s *MongoDB) Write(metrics []telegraf.Metric) error {
|
||||
ctx := context.Background()
|
||||
for _, metric := range metrics {
|
||||
if err := s.createTimeSeriesCollection(metric.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
bdoc := marshalMetric(metric)
|
||||
if err := s.insertDocument(ctx, metric.Name(), bdoc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
outputs.Add("mongodb", func() telegraf.Output { return &MongoDB{} })
|
||||
}
|
437
plugins/outputs/mongodb/mongodb_test.go
Normal file
437
plugins/outputs/mongodb/mongodb_test.go
Normal file
|
@ -0,0 +1,437 @@
|
|||
package mongodb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/common/tls"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func TestConnectAndWriteIntegrationNoAuth(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
servicePort := "27017"
|
||||
container := testutil.Container{
|
||||
Image: "mongo",
|
||||
ExposedPorts: []string{servicePort},
|
||||
WaitingFor: wait.ForAll(
|
||||
wait.NewHTTPStrategy("/").WithPort(nat.Port(servicePort)),
|
||||
wait.ForLog("Waiting for connections"),
|
||||
),
|
||||
}
|
||||
err := container.Start()
|
||||
require.NoError(t, err, "failed to start container")
|
||||
defer container.Terminate()
|
||||
|
||||
// Run test
|
||||
plugin := &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s",
|
||||
container.Address,
|
||||
container.Ports[servicePort],
|
||||
),
|
||||
AuthenticationType: "NONE",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
}
|
||||
|
||||
// validate config
|
||||
require.NoError(t, plugin.Init())
|
||||
require.NoError(t, plugin.Connect())
|
||||
require.NoError(t, plugin.Write(testutil.MockMetrics()))
|
||||
require.NoError(t, plugin.Close())
|
||||
}
|
||||
|
||||
func TestConnectAndWriteIntegrationSCRAMAuth(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
initdb, err := filepath.Abs("testdata/auth_scram/setup.js")
|
||||
require.NoError(t, err)
|
||||
|
||||
servicePort := "27017"
|
||||
container := testutil.Container{
|
||||
Image: "mongo",
|
||||
ExposedPorts: []string{servicePort},
|
||||
Files: map[string]string{
|
||||
"/docker-entrypoint-initdb.d/setup.js": initdb,
|
||||
},
|
||||
WaitingFor: wait.ForAll(
|
||||
wait.NewHTTPStrategy("/").WithPort(nat.Port(servicePort)),
|
||||
wait.ForLog("Waiting for connections").WithOccurrence(2),
|
||||
),
|
||||
}
|
||||
err = container.Start()
|
||||
require.NoError(t, err, "failed to start container")
|
||||
defer container.Terminate()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin *MongoDB
|
||||
connErrFunc func(t *testing.T, err error)
|
||||
}{
|
||||
{
|
||||
name: "success with scram authentication",
|
||||
plugin: &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s/admin",
|
||||
container.Address, container.Ports[servicePort]),
|
||||
AuthenticationType: "SCRAM",
|
||||
Username: config.NewSecret([]byte("root")),
|
||||
Password: config.NewSecret([]byte("changeme")),
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
},
|
||||
connErrFunc: func(t *testing.T, err error) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail with scram authentication bad password",
|
||||
plugin: &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s/admin",
|
||||
container.Address, container.Ports[servicePort]),
|
||||
AuthenticationType: "SCRAM",
|
||||
Username: config.NewSecret([]byte("root")),
|
||||
Password: config.NewSecret([]byte("root")),
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
ServerSelectTimeout: config.Duration(time.Duration(5) * time.Second),
|
||||
},
|
||||
connErrFunc: func(t *testing.T, err error) {
|
||||
require.Error(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// validate config
|
||||
err := tt.plugin.Init()
|
||||
require.NoError(t, err)
|
||||
|
||||
if err == nil {
|
||||
// connect
|
||||
err = tt.plugin.Connect()
|
||||
tt.connErrFunc(t, err)
|
||||
|
||||
if err == nil {
|
||||
// insert mock metrics
|
||||
err = tt.plugin.Write(testutil.MockMetrics())
|
||||
require.NoError(t, err)
|
||||
|
||||
// cleanup
|
||||
err = tt.plugin.Close()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectAndWriteIntegrationX509Auth(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
pki := testutil.NewPKI("../../../testutil/pki")
|
||||
|
||||
// bind mount files
|
||||
initdb, err := filepath.Abs("testdata/auth_x509/setup.js")
|
||||
require.NoError(t, err)
|
||||
cacert, err := filepath.Abs(pki.CACertPath())
|
||||
require.NoError(t, err)
|
||||
serverpem, err := filepath.Abs(pki.ServerCertAndKeyPath())
|
||||
require.NoError(t, err)
|
||||
|
||||
servicePort := "27017"
|
||||
container := testutil.Container{
|
||||
Image: "mongo",
|
||||
ExposedPorts: []string{servicePort},
|
||||
Files: map[string]string{
|
||||
"/docker-entrypoint-initdb.d/setup.js": initdb,
|
||||
"/cacert.pem": cacert,
|
||||
"/server.pem": serverpem,
|
||||
},
|
||||
Entrypoint: []string{
|
||||
"docker-entrypoint.sh",
|
||||
"--auth", "--setParameter", "authenticationMechanisms=MONGODB-X509",
|
||||
"--tlsMode", "preferTLS",
|
||||
"--tlsCAFile", "/cacert.pem",
|
||||
"--tlsCertificateKeyFile", "/server.pem",
|
||||
},
|
||||
WaitingFor: wait.ForAll(
|
||||
wait.NewHTTPStrategy("/").WithPort(nat.Port(servicePort)),
|
||||
wait.ForLog("Waiting for connections").WithOccurrence(2),
|
||||
),
|
||||
}
|
||||
err = container.Start()
|
||||
require.NoError(t, err, "failed to start container")
|
||||
defer container.Terminate()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin *MongoDB
|
||||
connErrFunc func(t *testing.T, err error)
|
||||
}{
|
||||
{
|
||||
name: "success with x509 authentication",
|
||||
plugin: &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s",
|
||||
container.Address, container.Ports[servicePort]),
|
||||
AuthenticationType: "X509",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
ServerSelectTimeout: config.Duration(time.Duration(5) * time.Second),
|
||||
TTL: config.Duration(time.Duration(5) * time.Minute),
|
||||
ClientConfig: tls.ClientConfig{
|
||||
TLSCA: pki.CACertPath(),
|
||||
TLSKey: pki.ClientCertAndKeyPath(),
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
},
|
||||
connErrFunc: func(t *testing.T, err error) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success with x509 authentication using encrypted key file",
|
||||
plugin: &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s",
|
||||
container.Address, container.Ports[servicePort]),
|
||||
AuthenticationType: "X509",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
ServerSelectTimeout: config.Duration(time.Duration(5) * time.Second),
|
||||
TTL: config.Duration(time.Duration(5) * time.Minute),
|
||||
ClientConfig: tls.ClientConfig{
|
||||
TLSCA: pki.CACertPath(),
|
||||
TLSKey: pki.ClientCertAndEncKeyPath(),
|
||||
TLSKeyPwd: "changeme",
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
},
|
||||
connErrFunc: func(t *testing.T, err error) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success with x509 authentication missing ca and using insceure tls",
|
||||
plugin: &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s",
|
||||
container.Address, container.Ports[servicePort]),
|
||||
AuthenticationType: "X509",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
ServerSelectTimeout: config.Duration(time.Duration(5) * time.Second),
|
||||
TTL: config.Duration(time.Duration(5) * time.Minute),
|
||||
ClientConfig: tls.ClientConfig{
|
||||
TLSKey: pki.ClientCertAndKeyPath(),
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
},
|
||||
connErrFunc: func(t *testing.T, err error) {
|
||||
require.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail with x509 authentication missing ca",
|
||||
plugin: &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s",
|
||||
container.Address, container.Ports[servicePort]),
|
||||
AuthenticationType: "X509",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
ServerSelectTimeout: config.Duration(time.Duration(5) * time.Second),
|
||||
TTL: config.Duration(time.Duration(5) * time.Minute),
|
||||
ClientConfig: tls.ClientConfig{
|
||||
TLSKey: pki.ClientCertAndKeyPath(),
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
},
|
||||
connErrFunc: func(t *testing.T, err error) {
|
||||
require.Error(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail with x509 authentication using encrypted key file",
|
||||
plugin: &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s",
|
||||
container.Address, container.Ports[servicePort]),
|
||||
AuthenticationType: "X509",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
ServerSelectTimeout: config.Duration(time.Duration(5) * time.Second),
|
||||
TTL: config.Duration(time.Duration(5) * time.Minute),
|
||||
ClientConfig: tls.ClientConfig{
|
||||
TLSCA: pki.CACertPath(),
|
||||
TLSKey: pki.ClientCertAndEncKeyPath(),
|
||||
TLSKeyPwd: "badpassword",
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
},
|
||||
connErrFunc: func(t *testing.T, err error) {
|
||||
require.Error(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail with x509 authentication using invalid ca",
|
||||
plugin: &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s",
|
||||
container.Address, container.Ports[servicePort]),
|
||||
AuthenticationType: "X509",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
ServerSelectTimeout: config.Duration(time.Duration(5) * time.Second),
|
||||
TTL: config.Duration(time.Duration(5) * time.Minute),
|
||||
ClientConfig: tls.ClientConfig{
|
||||
TLSCA: pki.ClientCertAndKeyPath(),
|
||||
TLSKey: pki.ClientCertAndKeyPath(),
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
},
|
||||
connErrFunc: func(t *testing.T, err error) {
|
||||
require.Error(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail with x509 authentication using invalid key",
|
||||
plugin: &MongoDB{
|
||||
Dsn: fmt.Sprintf("mongodb://%s:%s",
|
||||
container.Address, container.Ports[servicePort]),
|
||||
AuthenticationType: "X509",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
ServerSelectTimeout: config.Duration(time.Duration(5) * time.Second),
|
||||
TTL: config.Duration(time.Duration(5) * time.Minute),
|
||||
ClientConfig: tls.ClientConfig{
|
||||
TLSCA: pki.CACertPath(),
|
||||
TLSKey: pki.CACertPath(),
|
||||
InsecureSkipVerify: false,
|
||||
},
|
||||
},
|
||||
connErrFunc: func(t *testing.T, err error) {
|
||||
require.Error(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// validate config
|
||||
err := tt.plugin.Init()
|
||||
require.NoError(t, err)
|
||||
|
||||
if err == nil {
|
||||
// connect
|
||||
err = tt.plugin.Connect()
|
||||
tt.connErrFunc(t, err)
|
||||
|
||||
if err == nil {
|
||||
// insert mock metrics
|
||||
err = tt.plugin.Write(testutil.MockMetrics())
|
||||
require.NoError(t, err)
|
||||
|
||||
// cleanup
|
||||
err = tt.plugin.Close()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfiguration(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
plugin *MongoDB
|
||||
errFunc func(t *testing.T, err error)
|
||||
}{
|
||||
{
|
||||
name: "fail with invalid connection string",
|
||||
plugin: &MongoDB{
|
||||
Dsn: "asdf1234",
|
||||
AuthenticationType: "NONE",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
TTL: config.Duration(time.Duration(5) * time.Minute),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail with invalid metric granularity",
|
||||
plugin: &MongoDB{
|
||||
Dsn: "mongodb://localhost:27017",
|
||||
AuthenticationType: "NONE",
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "somerandomgranularitythatdoesntwork",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail with scram authentication missing username field",
|
||||
plugin: &MongoDB{
|
||||
Dsn: "mongodb://localhost:27017",
|
||||
AuthenticationType: "SCRAM",
|
||||
Password: config.NewSecret([]byte("somerandompasswordthatwontwork")),
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fail with scram authentication missing password field",
|
||||
plugin: &MongoDB{
|
||||
Dsn: "mongodb://localhost:27017",
|
||||
AuthenticationType: "SCRAM",
|
||||
Username: config.NewSecret([]byte("somerandomusernamethatwontwork")),
|
||||
MetricDatabase: "telegraf_test",
|
||||
MetricGranularity: "seconds",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// validate config
|
||||
err := tt.plugin.Init()
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
tests = []struct {
|
||||
name string
|
||||
plugin *MongoDB
|
||||
errFunc func(t *testing.T, err error)
|
||||
}{
|
||||
{
|
||||
name: "success init with missing metric database",
|
||||
plugin: &MongoDB{
|
||||
Dsn: "mongodb://localhost:27017",
|
||||
AuthenticationType: "NONE",
|
||||
MetricGranularity: "seconds",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success init missing metric granularity",
|
||||
plugin: &MongoDB{
|
||||
Dsn: "mongodb://localhost:27017",
|
||||
AuthenticationType: "NONE",
|
||||
MetricDatabase: "telegraf_test",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// validate config
|
||||
err := tt.plugin.Init()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
34
plugins/outputs/mongodb/sample.conf
Normal file
34
plugins/outputs/mongodb/sample.conf
Normal file
|
@ -0,0 +1,34 @@
|
|||
# A plugin that can transmit logs to mongodb
|
||||
[[outputs.mongodb]]
|
||||
# connection string examples for mongodb
|
||||
dsn = "mongodb://localhost:27017"
|
||||
# dsn = "mongodb://mongod1:27017,mongod2:27017,mongod3:27017/admin&replicaSet=myReplSet&w=1"
|
||||
|
||||
# overrides serverSelectionTimeoutMS in dsn if set
|
||||
# timeout = "30s"
|
||||
|
||||
# default authentication, optional
|
||||
# authentication = "NONE"
|
||||
|
||||
# for SCRAM-SHA-256 authentication
|
||||
# authentication = "SCRAM"
|
||||
# username = "root"
|
||||
# password = "***"
|
||||
|
||||
# for x509 certificate authentication
|
||||
# authentication = "X509"
|
||||
# tls_ca = "ca.pem"
|
||||
# tls_key = "client.pem"
|
||||
# # tls_key_pwd = "changeme" # required for encrypted tls_key
|
||||
# insecure_skip_verify = false
|
||||
|
||||
# database to store measurements and time series collections
|
||||
# database = "telegraf"
|
||||
|
||||
# granularity can be seconds, minutes, or hours.
|
||||
# configuring this value will be based on your input collection frequency.
|
||||
# see https://docs.mongodb.com/manual/core/timeseries-collections/#create-a-time-series-collection
|
||||
# granularity = "seconds"
|
||||
|
||||
# optionally set a TTL to automatically expire documents from the measurement collections.
|
||||
# ttl = "360h"
|
3
plugins/outputs/mongodb/testdata/auth_scram/setup.js
vendored
Normal file
3
plugins/outputs/mongodb/testdata/auth_scram/setup.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
const conn = new Mongo();
|
||||
const db = conn.getDB('admin');
|
||||
db.createUser({ user: 'root', pwd: 'changeme', roles: [{ role: 'root', db: 'admin' }] });
|
5
plugins/outputs/mongodb/testdata/auth_x509/setup.js
vendored
Normal file
5
plugins/outputs/mongodb/testdata/auth_x509/setup.js
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
const conn = new Mongo();
|
||||
const db = conn.getDB("admin");
|
||||
// createUser normally requires a password unless $external is used
|
||||
// the CN value was found via: openssl x509 -in client.pem -noout -subject -nameopt RFC2253 | sed 's/subject=//g'
|
||||
db.getSiblingDB("$external").runCommand({ createUser: "CN=localhost", roles: [{ role: "root", db: "admin" }] });
|
Loading…
Add table
Add a link
Reference in a new issue