From 908ad2f6ce5dcd5bded597c8e0d840666acfb297 Mon Sep 17 00:00:00 2001
From: Sven Rebhan <36194019+srebhan@users.noreply.github.com>
Date: Tue, 15 Jun 2021 21:10:52 +0200
Subject: [PATCH] Generic SQL input (#8735)
---
README.md | 1 +
docs/SQL_DRIVERS_INPUT.md | 43 ++
filter/filter.go | 22 +-
go.sum | 70 +++
internal/internal.go | 17 +
internal/type_conversions.go | 198 +++++++
plugins/inputs/all/all.go | 1 +
plugins/inputs/sql/README.md | 153 +++++
plugins/inputs/sql/drivers.go | 8 +
plugins/inputs/sql/drivers_sqlite.go | 8 +
plugins/inputs/sql/sql.go | 542 ++++++++++++++++++
plugins/inputs/sql/sql_test.go | 272 +++++++++
.../inputs/sql/testdata/mariadb/expected.sql | 36 ++
.../inputs/sql/testdata/postgres/expected.sql | 41 ++
14 files changed, 1409 insertions(+), 3 deletions(-)
create mode 100644 docs/SQL_DRIVERS_INPUT.md
create mode 100644 internal/type_conversions.go
create mode 100644 plugins/inputs/sql/README.md
create mode 100644 plugins/inputs/sql/drivers.go
create mode 100644 plugins/inputs/sql/drivers_sqlite.go
create mode 100644 plugins/inputs/sql/sql.go
create mode 100644 plugins/inputs/sql/sql_test.go
create mode 100644 plugins/inputs/sql/testdata/mariadb/expected.sql
create mode 100644 plugins/inputs/sql/testdata/postgres/expected.sql
diff --git a/README.md b/README.md
index 0702d6b4d..b579cdd81 100644
--- a/README.md
+++ b/README.md
@@ -324,6 +324,7 @@ For documentation on the latest development code see the [documentation index][d
* [snmp_trap](./plugins/inputs/snmp_trap)
* [socket_listener](./plugins/inputs/socket_listener)
* [solr](./plugins/inputs/solr)
+* [sql](./plugins/inputs/sql) (generic SQL query plugin)
* [sql server](./plugins/inputs/sqlserver) (microsoft)
* [stackdriver](./plugins/inputs/stackdriver) (Google Cloud Monitoring)
* [sql](./plugins/outputs/sql) (SQL generic output)
diff --git a/docs/SQL_DRIVERS_INPUT.md b/docs/SQL_DRIVERS_INPUT.md
new file mode 100644
index 000000000..81049fcee
--- /dev/null
+++ b/docs/SQL_DRIVERS_INPUT.md
@@ -0,0 +1,43 @@
+# Available SQL drivers for the SQL input plugin
+
+This is a list of available drivers for the SQL input plugin. The data-source-name (DSN) is driver specific and
+might change between versions. Please check the driver documentation for available options and the format.
+
+database | driver | aliases | example DSN | comment
+---------------------| ------------------------------------------------------| --------------- | -------------------------------------------------------------------------------------- | -------
+CockroachDB | [cockroach](https://github.com/jackc/pgx) | postgres
pgx | see _postgres_ driver | uses PostgresQL driver
+MariaDB | [maria](https://github.com/go-sql-driver/mysql) | mysql | see _mysql_ driver | uses MySQL driver
+Microsoft SQL Server | [sqlserver](https://github.com/denisenkom/go-mssqldb) | mssql | `username:password@host/instance?param1=value¶m2=value` | uses newer _sqlserver_ driver
+MySQL | [mysql](https://github.com/go-sql-driver/mysql) | | `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` | see [driver docs](https://github.com/go-sql-driver/mysql) for more information
+PostgreSQL | [postgres](https://github.com/jackc/pgx) | pgx | `[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&...]` | see [postgres docs](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING) for more information
+SQLite | [sqlite](https://gitlab.com/cznic/sqlite) | | `filename` | see [driver docu](https://pkg.go.dev/modernc.org/sqlite) for more information
+TiDB | [tidb](https://github.com/go-sql-driver/mysql) | mysql | see _mysql_ driver | uses MySQL driver
+
+## Comments
+
+### Driver aliases
+Some database drivers are supported though another driver (e.g. CockroachDB). For other databases we provide a more
+obvious name (e.g. postgres) compared to the driver name. For all of those drivers you might use an _alias_ name
+during configuration.
+
+### Example data-source-name DSN
+The given examples are just that, so please check the driver documentation for the exact format
+and available options and parameters. Please note that the format of a DSN might also change
+between driver version.
+
+### Type conversions
+Telegraf relies on type conversion of the database driver and/or the golang sql framework. In case you find
+any problem, please open an issue!
+
+## Help
+If nothing seems to work, you might find help in the telegraf forum or in the chat.
+
+### The documentation is wrong
+Please open an issue or even better send a pull-request!
+
+### I found a bug
+Please open an issue or even better send a pull-request!
+
+### My database is not supported
+We currently cannot support CGO drivers in telegraf! Please check if a **pure Go** driver for the [golang sql framework](https://golang.org/pkg/database/sql/) exists.
+If you found such a driver, please let us know by opening an issue or even better by sending a pull-request!
diff --git a/filter/filter.go b/filter/filter.go
index 29fcb8c4f..984fa3ed0 100644
--- a/filter/filter.go
+++ b/filter/filter.go
@@ -79,13 +79,24 @@ func compileFilterNoGlob(filters []string) Filter {
}
type IncludeExcludeFilter struct {
- include Filter
- exclude Filter
+ include Filter
+ exclude Filter
+ includeDefault bool
+ excludeDefault bool
}
func NewIncludeExcludeFilter(
include []string,
exclude []string,
+) (Filter, error) {
+ return NewIncludeExcludeFilterDefaults(include, exclude, true, false)
+}
+
+func NewIncludeExcludeFilterDefaults(
+ include []string,
+ exclude []string,
+ includeDefault bool,
+ excludeDefault bool,
) (Filter, error) {
in, err := Compile(include)
if err != nil {
@@ -97,7 +108,7 @@ func NewIncludeExcludeFilter(
return nil, err
}
- return &IncludeExcludeFilter{in, ex}, nil
+ return &IncludeExcludeFilter{in, ex, includeDefault, excludeDefault}, nil
}
func (f *IncludeExcludeFilter) Match(s string) bool {
@@ -105,12 +116,17 @@ func (f *IncludeExcludeFilter) Match(s string) bool {
if !f.include.Match(s) {
return false
}
+ } else if !f.includeDefault {
+ return false
}
if f.exclude != nil {
if f.exclude.Match(s) {
return false
}
+ } else if f.excludeDefault {
+ return false
}
+
return true
}
diff --git a/go.sum b/go.sum
index f4184b3ce..bd20f2842 100644
--- a/go.sum
+++ b/go.sum
@@ -110,23 +110,39 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go
github.com/Mellanox/rdmamap v0.0.0-20191106181932-7c3c4763a6ee h1:atI/FFjXh6hIVlPE1Jup9m8N4B9q/OSbMUe2EBahs+w=
github.com/Mellanox/rdmamap v0.0.0-20191106181932-7c3c4763a6ee/go.mod h1:jDA6v0TUYrFEIAE5uGJ29LQOeONIgMdP4Rkqb8HUnPM=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
+github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
+github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3 h1:mw6pDQqv38/WGF1cO/jF5t/jyAJ2yi7CmtFLLO5tGFI=
github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3 h1:mw6pDQqv38/WGF1cO/jF5t/jyAJ2yi7CmtFLLO5tGFI=
github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
+github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
+github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
+github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
github.com/Microsoft/hcsshim v0.8.16 h1:8/auA4LFIZFTGrqfKhGBSXwM6/4X1fHa/xniyEHu8ac=
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
+github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
@@ -246,8 +262,11 @@ github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
@@ -257,6 +276,8 @@ github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6L
github.com/caio/go-tdigest v3.1.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
+github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -287,12 +308,14 @@ github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABA
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
+github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
+github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68 h1:hkGVFjz+plgr5UfxZUTPFbUFIF/Km6/s+RVRIRHLrrY=
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
@@ -309,14 +332,17 @@ github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
+github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
github.com/containerd/containerd v1.5.0-beta.4 h1:zjz4MOAOFgdBlwid2nNUlJ3YLpVi/97L36lfMYJex60=
github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
+github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e h1:6JKvHHt396/qabvMhnhUZvWaHZzfVfldxE60TK8YLhg=
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
@@ -330,9 +356,11 @@ github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
+github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=
github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
+github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
@@ -405,16 +433,23 @@ github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v17.12.0-ce-rc1.0.20200706150819-a40b877fbb9e+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ=
github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
+github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
@@ -427,6 +462,7 @@ github.com/dropbox/godropbox v0.0.0-20180512210157-31879d3884b9/go.mod h1:glr97h
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.1.0 h1:ldKn47mFgWCoiJRXA32psdEACPKffX9O1Msh1K8M+f0=
github.com/dynatrace-oss/dynatrace-metric-utils-go v0.1.0/go.mod h1:qw0E9EJ0PnSlhWawDNuqE0zhc1hqOBUCFIAj3dd9DNw=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
@@ -614,6 +650,7 @@ github.com/gogo/googleapis v1.3.1/go.mod h1:d+q1s/xVJxZGKWwC/6UfPIF33J+G1Tq4GYv9
github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI=
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@@ -885,6 +922,7 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -1045,11 +1083,16 @@ github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hx
github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ=
github.com/moby/ipvs v1.0.1/go.mod h1:2pngiyseZbIKXNv7hsKj3O9UEz30c53MT9005gt2hxQ=
github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM=
+github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM=
github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=
+github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=
+github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
+github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
+github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
@@ -1107,6 +1150,7 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -1131,17 +1175,30 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runc v1.0.0-rc93 h1:x2UMpOOVf3kQ8arv/EsDGwim8PTNqzL1/EYDr/+scOM=
github.com/opencontainers/runc v1.0.0-rc93 h1:x2UMpOOVf3kQ8arv/EsDGwim8PTNqzL1/EYDr/+scOM=
github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
+github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
+github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
+github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
+github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
@@ -1230,6 +1287,7 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/prometheus v1.8.2-0.20200911110723-e83ef207b6c2 h1:IB/5RJRcJiR/YzKs4Aou86s/RaMepZOZVCArYNHJHWc=
@@ -1296,6 +1354,7 @@ github.com/signalfx/sapm-proto v0.4.0/go.mod h1:x3gtwJ1GRejtkghB4nYpwixh2zqJrLbP
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -1307,6 +1366,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
@@ -1330,6 +1391,7 @@ github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bd
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
@@ -1586,6 +1648,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1650,6 +1713,8 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1678,6 +1743,7 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200821140526-fda516888d29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200821140526-fda516888d29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1689,11 +1755,15 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/internal/internal.go b/internal/internal.go
index 055ea361c..4441e9acf 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -297,8 +297,25 @@ func parseComponents(timestamp interface{}) (int64, int64, error) {
return 0, 0, err
}
return integer, 0, nil
+ case int8:
+ return int64(ts), 0, nil
+ case int16:
+ return int64(ts), 0, nil
+ case int32:
+ return int64(ts), 0, nil
case int64:
return ts, 0, nil
+ case uint8:
+ return int64(ts), 0, nil
+ case uint16:
+ return int64(ts), 0, nil
+ case uint32:
+ return int64(ts), 0, nil
+ case uint64:
+ return int64(ts), 0, nil
+ case float32:
+ integer, fractional := math.Modf(float64(ts))
+ return int64(integer), int64(fractional * 1e9), nil
case float64:
integer, fractional := math.Modf(ts)
return int64(integer), int64(fractional * 1e9), nil
diff --git a/internal/type_conversions.go b/internal/type_conversions.go
new file mode 100644
index 000000000..ed4ed374a
--- /dev/null
+++ b/internal/type_conversions.go
@@ -0,0 +1,198 @@
+package internal
+
+import (
+ "fmt"
+ "strconv"
+)
+
+func ToString(value interface{}) (string, error) {
+ switch v := value.(type) {
+ case string:
+ return v, nil
+ case []byte:
+ return string(v), nil
+ case int:
+ return strconv.FormatInt(int64(v), 10), nil
+ case int8:
+ return strconv.FormatInt(int64(v), 10), nil
+ case int16:
+ return strconv.FormatInt(int64(v), 10), nil
+ case int32:
+ return strconv.FormatInt(int64(v), 10), nil
+ case int64:
+ return strconv.FormatInt(v, 10), nil
+ case uint:
+ return strconv.FormatUint(uint64(v), 10), nil
+ case uint8:
+ return strconv.FormatUint(uint64(v), 10), nil
+ case uint16:
+ return strconv.FormatUint(uint64(v), 10), nil
+ case uint32:
+ return strconv.FormatUint(uint64(v), 10), nil
+ case uint64:
+ return strconv.FormatUint(v, 10), nil
+ case float32:
+ return strconv.FormatFloat(float64(v), 'f', -1, 32), nil
+ case float64:
+ return strconv.FormatFloat(v, 'f', -1, 64), nil
+ case bool:
+ return strconv.FormatBool(v), nil
+ case fmt.Stringer:
+ return v.String(), nil
+ case nil:
+ return "", nil
+ }
+ return "", fmt.Errorf("type \"%T\" unsupported", value)
+}
+
+func ToFloat64(value interface{}) (float64, error) {
+ switch v := value.(type) {
+ case string:
+ return strconv.ParseFloat(v, 64)
+ case []byte:
+ return strconv.ParseFloat(string(v), 64)
+ case fmt.Stringer:
+ return strconv.ParseFloat(v.String(), 64)
+ case int:
+ return float64(v), nil
+ case int8:
+ return float64(v), nil
+ case int16:
+ return float64(v), nil
+ case int32:
+ return float64(v), nil
+ case int64:
+ return float64(v), nil
+ case uint:
+ return float64(v), nil
+ case uint8:
+ return float64(v), nil
+ case uint16:
+ return float64(v), nil
+ case uint32:
+ return float64(v), nil
+ case uint64:
+ return float64(v), nil
+ case float32:
+ return float64(v), nil
+ case float64:
+ return v, nil
+ case nil:
+ return 0, nil
+ }
+ return 0, fmt.Errorf("type \"%T\" unsupported", value)
+}
+
+func ToInt64(value interface{}) (int64, error) {
+ switch v := value.(type) {
+ case string:
+ return strconv.ParseInt(v, 10, 64)
+ case []byte:
+ return strconv.ParseInt(string(v), 10, 64)
+ case fmt.Stringer:
+ return strconv.ParseInt(v.String(), 10, 64)
+ case int:
+ return int64(v), nil
+ case int8:
+ return int64(v), nil
+ case int16:
+ return int64(v), nil
+ case int32:
+ return int64(v), nil
+ case int64:
+ return v, nil
+ case uint:
+ return int64(v), nil
+ case uint8:
+ return int64(v), nil
+ case uint16:
+ return int64(v), nil
+ case uint32:
+ return int64(v), nil
+ case uint64:
+ return int64(v), nil
+ case float32:
+ return int64(v), nil
+ case float64:
+ return int64(v), nil
+ case nil:
+ return 0, nil
+ }
+ return 0, fmt.Errorf("type \"%T\" unsupported", value)
+}
+
+func ToUint64(value interface{}) (uint64, error) {
+ switch v := value.(type) {
+ case string:
+ return strconv.ParseUint(v, 10, 64)
+ case []byte:
+ return strconv.ParseUint(string(v), 10, 64)
+ case fmt.Stringer:
+ return strconv.ParseUint(v.String(), 10, 64)
+ case int:
+ return uint64(v), nil
+ case int8:
+ return uint64(v), nil
+ case int16:
+ return uint64(v), nil
+ case int32:
+ return uint64(v), nil
+ case int64:
+ return uint64(v), nil
+ case uint:
+ return uint64(v), nil
+ case uint8:
+ return uint64(v), nil
+ case uint16:
+ return uint64(v), nil
+ case uint32:
+ return uint64(v), nil
+ case uint64:
+ return v, nil
+ case float32:
+ return uint64(v), nil
+ case float64:
+ return uint64(v), nil
+ case nil:
+ return 0, nil
+ }
+ return 0, fmt.Errorf("type \"%T\" unsupported", value)
+}
+
+func ToBool(value interface{}) (bool, error) {
+ switch v := value.(type) {
+ case string:
+ return strconv.ParseBool(v)
+ case []byte:
+ return strconv.ParseBool(string(v))
+ case fmt.Stringer:
+ return strconv.ParseBool(v.String())
+ case int:
+ return v > 0, nil
+ case int8:
+ return v > 0, nil
+ case int16:
+ return v > 0, nil
+ case int32:
+ return v > 0, nil
+ case int64:
+ return v > 0, nil
+ case uint:
+ return v > 0, nil
+ case uint8:
+ return v > 0, nil
+ case uint16:
+ return v > 0, nil
+ case uint32:
+ return v > 0, nil
+ case uint64:
+ return v > 0, nil
+ case float32:
+ return v > 0, nil
+ case float64:
+ return v > 0, nil
+ case nil:
+ return false, nil
+ }
+ return false, fmt.Errorf("type \"%T\" unsupported", value)
+}
diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go
index aa273a4aa..7c4e0bcf4 100644
--- a/plugins/inputs/all/all.go
+++ b/plugins/inputs/all/all.go
@@ -167,6 +167,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/inputs/snmp_trap"
_ "github.com/influxdata/telegraf/plugins/inputs/socket_listener"
_ "github.com/influxdata/telegraf/plugins/inputs/solr"
+ _ "github.com/influxdata/telegraf/plugins/inputs/sql"
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
_ "github.com/influxdata/telegraf/plugins/inputs/stackdriver"
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
diff --git a/plugins/inputs/sql/README.md b/plugins/inputs/sql/README.md
new file mode 100644
index 000000000..9c002df18
--- /dev/null
+++ b/plugins/inputs/sql/README.md
@@ -0,0 +1,153 @@
+# SQL Input Plugin
+
+This plugin reads metrics from performing SQL queries against a SQL server. Different server
+types are supported and their settings might differ (especially the connection parameters).
+Please check the list of [supported SQL drivers](../../../docs/SQL_DRIVERS_INPUT.md) for the
+`driver` name and options for the data-source-name (`dsn`) options.
+
+### Configuration
+
+This section contains the default TOML to configure the plugin. You can
+generate it using `telegraf --usage `.
+
+```toml
+[[inputs.sql]]
+ ## Database Driver
+ ## See https://github.com/influxdata/telegraf/blob/master/docs/SQL_DRIVERS_INPUT.md for
+ ## a list of supported drivers.
+ driver = "mysql"
+
+ ## Data source name for connecting
+ ## The syntax and supported options depends on selected driver.
+ dsn = "username:password@mysqlserver:3307/dbname?param=value"
+
+ ## Timeout for any operation
+ # timeout = "5s"
+
+ ## Connection time limits
+ ## By default the maximum idle time and maximum lifetime of a connection is unlimited, i.e. the connections
+ ## will not be closed automatically. If you specify a positive time, the connections will be closed after
+ ## idleing or existing for at least that amount of time, respectively.
+ # connection_max_idle_time = "0s"
+ # connection_max_life_time = "0s"
+
+ ## Connection count limits
+ ## By default the number of open connections is not limited and the number of maximum idle connections
+ ## will be inferred from the number of queries specified. If you specify a positive number for any of the
+ ## two options, connections will be closed when reaching the specified limit. The number of idle connections
+ ## will be clipped to the maximum number of connections limit if any.
+ # connection_max_open = 0
+ # connection_max_idle = auto
+
+ [[inputs.sql.query]]
+ ## Query to perform on the server
+ query="SELECT user,state,latency,score FROM Scoreboard WHERE application > 0"
+ ## Alternatively to specifying the query directly you can select a file here containing the SQL query.
+ ## Only one of 'query' and 'query_script' can be specified!
+ # query_script = "/path/to/sql/script.sql"
+
+ ## Name of the measurement
+ ## In case both measurement and 'measurement_col' are given, the latter takes precedence.
+ # measurement = "sql"
+
+ ## Column name containing the name of the measurement
+ ## If given, this will take precedence over the 'measurement' setting. In case a query result
+ ## does not contain the specified column, we fall-back to the 'measurement' setting.
+ # measurement_column = ""
+
+ ## Column name containing the time of the measurement
+ ## If ommited, the time of the query will be used.
+ # time_column = ""
+
+ ## Format of the time contained in 'time_col'
+ ## The time must be 'unix', 'unix_ms', 'unix_us', 'unix_ns', or a golang time format.
+ ## See https://golang.org/pkg/time/#Time.Format for details.
+ # time_format = "unix"
+
+ ## Column names containing tags
+ ## An empty include list will reject all columns and an empty exclude list will not exclude any column.
+ ## I.e. by default no columns will be returned as tag and the tags are empty.
+ # tag_columns_include = []
+ # tag_columns_exclude = []
+
+ ## Column names containing fields (explicit types)
+ ## Convert the given columns to the corresponding type. Explicit type conversions take precedence over
+ ## the automatic (driver-based) conversion below.
+ ## NOTE: Columns should not be specified for multiple types or the resulting type is undefined.
+ # field_columns_float = []
+ # field_columns_int = []
+ # field_columns_uint = []
+ # field_columns_bool = []
+ # field_columns_string = []
+
+ ## Column names containing fields (automatic types)
+ ## An empty include list is equivalent to '[*]' and all returned columns will be accepted. An empty
+ ## exclude list will not exclude any column. I.e. by default all columns will be returned as fields.
+ ## NOTE: We rely on the database driver to perform automatic datatype conversion.
+ # field_columns_include = []
+ # field_columns_exclude = []
+```
+
+### Options
+#### Driver
+The `driver` and `dsn` options specify how to connect to the database. As especially the `dsn` format and
+values vary with the `driver` refer to the list of [supported SQL drivers](../../../docs/SQL_DRIVERS_INPUT.md) for possible values and more details.
+
+#### Connection limits
+With these options you can limit the number of connections kept open by this plugin. Details about the exact
+workings can be found in the [golang sql documentation](https://golang.org/pkg/database/sql/#DB.SetConnMaxIdleTime).
+
+#### Query sections
+Multiple `query` sections can be specified for this plugin. Each specified query will first be prepared on the server
+and then executed in every interval using the column mappings specified. Please note that `tag` and `field` columns
+are not exclusive, i.e. a column can be added to both. When using both `include` and `exclude` lists, the `exclude`
+list takes precedence over the `include` list. I.e. given you specify `foo` in both lists, `foo` will _never_ pass
+the filter. In case any the columns specified in `measurement_col` or `time_col` are _not_ returned by the query,
+the plugin falls-back to the documented defaults. Fields or tags specified in the includes of the options but missing
+in the returned query are silently ignored.
+
+### Types
+This plugin relies on the driver to do the type conversion. For the different properties of the metric the following
+types are accepted.
+
+#### Measurement
+Only columns of type `string` are accepted.
+
+#### Time
+For the metric time columns of type `time` are accepted directly. For numeric columns, `time_format` should be set
+to any of `unix`, `unix_ms`, `unix_ns` or `unix_us` accordingly. By default the a timestamp in `unix` format is
+expected. For string columns, please specify the `time_format` accordingly.
+See the [golang time documentation](https://golang.org/pkg/time/#Time.Format) for details.
+
+#### Tags
+For tags columns with textual values (`string` and `bytes`), signed and unsigned integers (8, 16, 32 and 64 bit),
+floating-point (32 and 64 bit), `boolean` and `time` values are accepted. Those values will be converted to string.
+
+#### Fields
+For fields columns with textual values (`string` and `bytes`), signed and unsigned integers (8, 16, 32 and 64 bit),
+floating-point (32 and 64 bit), `boolean` and `time` values are accepted. Here `bytes` will be converted to `string`,
+signed and unsigned integer values will be converted to `int64` or `uint64` respectively. Floating-point values are converted to `float64` and `time` is converted to a nanosecond timestamp of type `int64`.
+
+### Example Output
+Using the [MariaDB sample database](https://www.mariadbtutorial.com/getting-started/mariadb-sample-database) and the
+configuration
+```toml
+[[inputs.sql]]
+ driver = "mysql"
+ dsn = "root:password@/nation"
+
+ [[inputs.sql.query]]
+ query="SELECT * FROM guests"
+ measurement = "nation"
+ tag_cols_include = ["name"]
+ field_cols_exclude = ["name"]
+```
+
+Telegraf will output the following metrics
+```
+nation,host=Hugin,name=John guest_id=1i 1611332164000000000
+nation,host=Hugin,name=Jane guest_id=2i 1611332164000000000
+nation,host=Hugin,name=Jean guest_id=3i 1611332164000000000
+nation,host=Hugin,name=Storm guest_id=4i 1611332164000000000
+nation,host=Hugin,name=Beast guest_id=5i 1611332164000000000
+```
diff --git a/plugins/inputs/sql/drivers.go b/plugins/inputs/sql/drivers.go
new file mode 100644
index 000000000..09af9bfc8
--- /dev/null
+++ b/plugins/inputs/sql/drivers.go
@@ -0,0 +1,8 @@
+package sql
+
+import (
+ // Blank imports to register the drivers
+ _ "github.com/denisenkom/go-mssqldb"
+ _ "github.com/go-sql-driver/mysql"
+ _ "github.com/jackc/pgx/v4/stdlib"
+)
diff --git a/plugins/inputs/sql/drivers_sqlite.go b/plugins/inputs/sql/drivers_sqlite.go
new file mode 100644
index 000000000..4c9e56a8c
--- /dev/null
+++ b/plugins/inputs/sql/drivers_sqlite.go
@@ -0,0 +1,8 @@
+// +build linux,freebsd
+// +build !mips !mips64
+
+package sql
+
+import (
+ _ "modernc.org/sqlite"
+)
diff --git a/plugins/inputs/sql/sql.go b/plugins/inputs/sql/sql.go
new file mode 100644
index 000000000..383f04c40
--- /dev/null
+++ b/plugins/inputs/sql/sql.go
@@ -0,0 +1,542 @@
+package sql
+
+import (
+ "context"
+ dbsql "database/sql"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/influxdata/telegraf"
+ "github.com/influxdata/telegraf/config"
+ "github.com/influxdata/telegraf/filter"
+ "github.com/influxdata/telegraf/internal"
+ "github.com/influxdata/telegraf/internal/choice"
+ "github.com/influxdata/telegraf/plugins/inputs"
+)
+
+const sampleConfig = `
+ ## Database Driver
+ ## See https://github.com/influxdata/telegraf/blob/master/docs/SQL_DRIVERS_INPUT.md for
+ ## a list of supported drivers.
+ driver = "mysql"
+
+ ## Data source name for connecting
+ ## The syntax and supported options depends on selected driver.
+ dsn = "username:password@mysqlserver:3307/dbname?param=value"
+
+ ## Timeout for any operation
+ # timeout = "5s"
+
+ ## Connection time limits
+ ## By default the maximum idle time and maximum lifetime of a connection is unlimited, i.e. the connections
+ ## will not be closed automatically. If you specify a positive time, the connections will be closed after
+ ## idleing or existing for at least that amount of time, respectively.
+ # connection_max_idle_time = "0s"
+ # connection_max_life_time = "0s"
+
+ ## Connection count limits
+ ## By default the number of open connections is not limited and the number of maximum idle connections
+ ## will be inferred from the number of queries specified. If you specify a positive number for any of the
+ ## two options, connections will be closed when reaching the specified limit. The number of idle connections
+ ## will be clipped to the maximum number of connections limit if any.
+ # connection_max_open = 0
+ # connection_max_idle = auto
+
+ [[inputs.sql.query]]
+ ## Query to perform on the server
+ query="SELECT user,state,latency,score FROM Scoreboard WHERE application > 0"
+ ## Alternatively to specifying the query directly you can select a file here containing the SQL query.
+ ## Only one of 'query' and 'query_script' can be specified!
+ # query_script = "/path/to/sql/script.sql"
+
+ ## Name of the measurement
+ ## In case both measurement and 'measurement_col' are given, the latter takes precedence.
+ # measurement = "sql"
+
+ ## Column name containing the name of the measurement
+ ## If given, this will take precedence over the 'measurement' setting. In case a query result
+ ## does not contain the specified column, we fall-back to the 'measurement' setting.
+ # measurement_column = ""
+
+ ## Column name containing the time of the measurement
+ ## If ommited, the time of the query will be used.
+ # time_column = ""
+
+ ## Format of the time contained in 'time_col'
+ ## The time must be 'unix', 'unix_ms', 'unix_us', 'unix_ns', or a golang time format.
+ ## See https://golang.org/pkg/time/#Time.Format for details.
+ # time_format = "unix"
+
+ ## Column names containing tags
+ ## An empty include list will reject all columns and an empty exclude list will not exclude any column.
+ ## I.e. by default no columns will be returned as tag and the tags are empty.
+ # tag_columns_include = []
+ # tag_columns_exclude = []
+
+ ## Column names containing fields (explicit types)
+ ## Convert the given columns to the corresponding type. Explicit type conversions take precedence over
+ ## the automatic (driver-based) conversion below.
+ ## NOTE: Columns should not be specified for multiple types or the resulting type is undefined.
+ # field_columns_float = []
+ # field_columns_int = []
+ # field_columns_uint = []
+ # field_columns_bool = []
+ # field_columns_string = []
+
+ ## Column names containing fields (automatic types)
+ ## An empty include list is equivalent to '[*]' and all returned columns will be accepted. An empty
+ ## exclude list will not exclude any column. I.e. by default all columns will be returned as fields.
+ ## NOTE: We rely on the database driver to perform automatic datatype conversion.
+ # field_columns_include = []
+ # field_columns_exclude = []
+`
+
+const magicIdleCount int = (-int(^uint(0) >> 1))
+
+type Query struct {
+ Query string `toml:"query"`
+ Script string `toml:"query_script"`
+ Measurement string `toml:"measurement"`
+ MeasurementColumn string `toml:"measurement_column"`
+ TimeColumn string `toml:"time_column"`
+ TimeFormat string `toml:"time_format"`
+ TagColumnsInclude []string `toml:"tag_columns_include"`
+ TagColumnsExclude []string `toml:"tag_columns_exclude"`
+ FieldColumnsInclude []string `toml:"field_columns_include"`
+ FieldColumnsExclude []string `toml:"field_columns_exclude"`
+ FieldColumnsFloat []string `toml:"field_columns_float"`
+ FieldColumnsInt []string `toml:"field_columns_int"`
+ FieldColumnsUint []string `toml:"field_columns_uint"`
+ FieldColumnsBool []string `toml:"field_columns_bool"`
+ FieldColumnsString []string `toml:"field_columns_string"`
+
+ statement *dbsql.Stmt
+ tagFilter filter.Filter
+ fieldFilter filter.Filter
+ fieldFilterFloat filter.Filter
+ fieldFilterInt filter.Filter
+ fieldFilterUint filter.Filter
+ fieldFilterBool filter.Filter
+ fieldFilterString filter.Filter
+}
+
+func (q *Query) parse(ctx context.Context, acc telegraf.Accumulator, rows *dbsql.Rows, t time.Time) (int, error) {
+ columnNames, err := rows.Columns()
+ if err != nil {
+ return 0, err
+ }
+
+ // Prepare the list of datapoints according to the received row
+ columnData := make([]interface{}, len(columnNames))
+ columnDataPtr := make([]interface{}, len(columnNames))
+
+ for i := range columnData {
+ columnDataPtr[i] = &columnData[i]
+ }
+
+ rowCount := 0
+ for rows.Next() {
+ measurement := q.Measurement
+ timestamp := t
+ tags := make(map[string]string)
+ fields := make(map[string]interface{}, len(columnNames))
+
+ // Do the parsing with (hopefully) automatic type conversion
+ if err := rows.Scan(columnDataPtr...); err != nil {
+ return 0, err
+ }
+
+ for i, name := range columnNames {
+ if q.MeasurementColumn != "" && name == q.MeasurementColumn {
+ var ok bool
+ if measurement, ok = columnData[i].(string); !ok {
+ return 0, fmt.Errorf("measurement column type \"%T\" unsupported", columnData[i])
+ }
+ }
+
+ if q.TimeColumn != "" && name == q.TimeColumn {
+ var fieldvalue interface{}
+ var skipParsing bool
+
+ switch v := columnData[i].(type) {
+ case string, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
+ fieldvalue = v
+ case []byte:
+ fieldvalue = string(v)
+ case time.Time:
+ timestamp = v
+ skipParsing = true
+ case fmt.Stringer:
+ fieldvalue = v.String()
+ default:
+ return 0, fmt.Errorf("time column %q of type \"%T\" unsupported", name, columnData[i])
+ }
+ if !skipParsing {
+ if timestamp, err = internal.ParseTimestamp(q.TimeFormat, fieldvalue, ""); err != nil {
+ return 0, fmt.Errorf("parsing time failed: %v", err)
+ }
+ }
+ }
+
+ if q.tagFilter.Match(name) {
+ tagvalue, err := internal.ToString(columnData[i])
+ if err != nil {
+ return 0, fmt.Errorf("converting tag column %q failed: %v", name, err)
+ }
+ if v := strings.TrimSpace(tagvalue); v != "" {
+ tags[name] = v
+ }
+ }
+
+ // Explicit type conversions take precedence
+ if q.fieldFilterFloat.Match(name) {
+ v, err := internal.ToFloat64(columnData[i])
+ if err != nil {
+ return 0, fmt.Errorf("converting field column %q to float failed: %v", name, err)
+ }
+ fields[name] = v
+ continue
+ }
+
+ if q.fieldFilterInt.Match(name) {
+ v, err := internal.ToInt64(columnData[i])
+ if err != nil {
+ return 0, fmt.Errorf("converting field column %q to int failed: %v", name, err)
+ }
+ fields[name] = v
+ continue
+ }
+
+ if q.fieldFilterUint.Match(name) {
+ v, err := internal.ToUint64(columnData[i])
+ if err != nil {
+ return 0, fmt.Errorf("converting field column %q to uint failed: %v", name, err)
+ }
+ fields[name] = v
+ continue
+ }
+
+ if q.fieldFilterBool.Match(name) {
+ v, err := internal.ToBool(columnData[i])
+ if err != nil {
+ return 0, fmt.Errorf("converting field column %q to bool failed: %v", name, err)
+ }
+ fields[name] = v
+ continue
+ }
+
+ if q.fieldFilterString.Match(name) {
+ v, err := internal.ToString(columnData[i])
+ if err != nil {
+ return 0, fmt.Errorf("converting field column %q to string failed: %v", name, err)
+ }
+ fields[name] = v
+ continue
+ }
+
+ // Try automatic conversion for all remaining fields
+ if q.fieldFilter.Match(name) {
+ var fieldvalue interface{}
+ switch v := columnData[i].(type) {
+ case string, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, bool:
+ fieldvalue = v
+ case []byte:
+ fieldvalue = string(v)
+ case time.Time:
+ fieldvalue = v.UnixNano()
+ case nil:
+ fieldvalue = nil
+ case fmt.Stringer:
+ fieldvalue = v.String()
+ default:
+ return 0, fmt.Errorf("field column %q of type \"%T\" unsupported", name, columnData[i])
+ }
+ if fieldvalue != nil {
+ fields[name] = fieldvalue
+ }
+ }
+ }
+ acc.AddFields(measurement, fields, tags, timestamp)
+ rowCount++
+ }
+
+ if err := rows.Err(); err != nil {
+ return rowCount, err
+ }
+
+ return rowCount, nil
+}
+
+type SQL struct {
+ Driver string `toml:"driver"`
+ Dsn string `toml:"dsn"`
+ Timeout config.Duration `toml:"timeout"`
+ MaxIdleTime config.Duration `toml:"connection_max_idle_time"`
+ MaxLifetime config.Duration `toml:"connection_max_life_time"`
+ MaxOpenConnections int `toml:"connection_max_open"`
+ MaxIdleConnections int `toml:"connection_max_idle"`
+ Queries []Query `toml:"query"`
+ Log telegraf.Logger `toml:"-"`
+
+ driverName string
+ db *dbsql.DB
+}
+
+func (s *SQL) Description() string {
+ return `Read metrics from SQL queries`
+}
+
+func (s *SQL) SampleConfig() string {
+ return sampleConfig
+}
+
+func (s *SQL) Init() error {
+ // Option handling
+ if s.Driver == "" {
+ return errors.New("missing SQL driver option")
+ }
+
+ if s.Dsn == "" {
+ return errors.New("missing data source name (DSN) option")
+ }
+
+ if s.Timeout <= 0 {
+ s.Timeout = config.Duration(5 * time.Second)
+ }
+
+ if s.MaxIdleConnections == magicIdleCount {
+ // Determine the number by the number of queries + the golang default value
+ s.MaxIdleConnections = len(s.Queries) + 2
+ }
+
+ for i, q := range s.Queries {
+ if q.Query == "" && q.Script == "" {
+ return errors.New("neither 'query' nor 'query_script' specified")
+ }
+
+ if q.Query != "" && q.Script != "" {
+ return errors.New("only one of 'query' and 'query_script' can be specified")
+ }
+
+ // In case we got a script, we should read the query now.
+ if q.Script != "" {
+ query, err := ioutil.ReadFile(q.Script)
+ if err != nil {
+ return fmt.Errorf("reading script %q failed: %v", q.Script, err)
+ }
+ s.Queries[i].Query = string(query)
+ }
+
+ // Time format
+ if q.TimeFormat == "" {
+ s.Queries[i].TimeFormat = "unix"
+ }
+
+ // Compile the tag-filter
+ tagfilter, err := filter.NewIncludeExcludeFilterDefaults(q.TagColumnsInclude, q.TagColumnsExclude, false, false)
+ if err != nil {
+ return fmt.Errorf("creating tag filter failed: %v", err)
+ }
+ s.Queries[i].tagFilter = tagfilter
+
+ // Compile the explicit type field-filter
+ fieldfilterFloat, err := filter.NewIncludeExcludeFilterDefaults(q.FieldColumnsFloat, nil, false, false)
+ if err != nil {
+ return fmt.Errorf("creating field filter for float failed: %v", err)
+ }
+ s.Queries[i].fieldFilterFloat = fieldfilterFloat
+
+ fieldfilterInt, err := filter.NewIncludeExcludeFilterDefaults(q.FieldColumnsInt, nil, false, false)
+ if err != nil {
+ return fmt.Errorf("creating field filter for int failed: %v", err)
+ }
+ s.Queries[i].fieldFilterInt = fieldfilterInt
+
+ fieldfilterUint, err := filter.NewIncludeExcludeFilterDefaults(q.FieldColumnsUint, nil, false, false)
+ if err != nil {
+ return fmt.Errorf("creating field filter for uint failed: %v", err)
+ }
+ s.Queries[i].fieldFilterUint = fieldfilterUint
+
+ fieldfilterBool, err := filter.NewIncludeExcludeFilterDefaults(q.FieldColumnsBool, nil, false, false)
+ if err != nil {
+ return fmt.Errorf("creating field filter for bool failed: %v", err)
+ }
+ s.Queries[i].fieldFilterBool = fieldfilterBool
+
+ fieldfilterString, err := filter.NewIncludeExcludeFilterDefaults(q.FieldColumnsString, nil, false, false)
+ if err != nil {
+ return fmt.Errorf("creating field filter for string failed: %v", err)
+ }
+ s.Queries[i].fieldFilterString = fieldfilterString
+
+ // Compile the field-filter
+ fieldfilter, err := filter.NewIncludeExcludeFilter(q.FieldColumnsInclude, q.FieldColumnsExclude)
+ if err != nil {
+ return fmt.Errorf("creating field filter failed: %v", err)
+ }
+ s.Queries[i].fieldFilter = fieldfilter
+
+ if q.Measurement == "" {
+ s.Queries[i].Measurement = "sql"
+ }
+ }
+
+ // Derive the sql-framework driver name from our config name. This abstracts the actual driver
+ // from the database-type the user wants.
+ aliases := map[string]string{
+ "cockroach": "pgx",
+ "tidb": "mysql",
+ "mssql": "sqlserver",
+ "maria": "mysql",
+ "postgres": "pgx",
+ }
+ s.driverName = s.Driver
+ if driver, ok := aliases[s.Driver]; ok {
+ s.driverName = driver
+ }
+
+ availDrivers := dbsql.Drivers()
+ if !choice.Contains(s.driverName, availDrivers) {
+ for d, r := range aliases {
+ if choice.Contains(r, availDrivers) {
+ availDrivers = append(availDrivers, d)
+ }
+ }
+
+ // Sort the list of drivers and make them unique
+ sort.Strings(availDrivers)
+ last := 0
+ for _, d := range availDrivers {
+ if d != availDrivers[last] {
+ last++
+ availDrivers[last] = d
+ }
+ }
+ availDrivers = availDrivers[:last+1]
+
+ return fmt.Errorf("driver %q not supported use one of %v", s.Driver, availDrivers)
+ }
+
+ return nil
+}
+
+func (s *SQL) Start(_ telegraf.Accumulator) error {
+ var err error
+
+ // Connect to the database server
+ s.Log.Debugf("Connecting to %q...", s.Dsn)
+ s.db, err = dbsql.Open(s.driverName, s.Dsn)
+ if err != nil {
+ return err
+ }
+
+ // Set the connection limits
+ // s.db.SetConnMaxIdleTime(time.Duration(s.MaxIdleTime)) // Requires go >= 1.15
+ s.db.SetConnMaxLifetime(time.Duration(s.MaxLifetime))
+ s.db.SetMaxOpenConns(s.MaxOpenConnections)
+ s.db.SetMaxIdleConns(s.MaxIdleConnections)
+
+ // Test if the connection can be established
+ s.Log.Debugf("Testing connectivity...")
+ ctx, cancel := context.WithTimeout(context.Background(), time.Duration(s.Timeout))
+ err = s.db.PingContext(ctx)
+ cancel()
+ if err != nil {
+ return fmt.Errorf("connecting to database failed: %v", err)
+ }
+
+ // Prepare the statements
+ for i, q := range s.Queries {
+ s.Log.Debugf("Preparing statement %q...", q.Query)
+ ctx, cancel := context.WithTimeout(context.Background(), time.Duration(s.Timeout))
+ stmt, err := s.db.PrepareContext(ctx, q.Query) //nolint:sqlclosecheck // Closed in Stop()
+ cancel()
+ if err != nil {
+ return fmt.Errorf("preparing query %q failed: %v", q.Query, err)
+ }
+ s.Queries[i].statement = stmt
+ }
+
+ return nil
+}
+
+func (s *SQL) Stop() {
+ // Free the statements
+ for _, q := range s.Queries {
+ if q.statement != nil {
+ if err := q.statement.Close(); err != nil {
+ s.Log.Errorf("closing statement for query %q failed: %v", q.Query, err)
+ }
+ }
+ }
+
+ // Close the connection to the server
+ if s.db != nil {
+ if err := s.db.Close(); err != nil {
+ s.Log.Errorf("closing database connection failed: %v", err)
+ }
+ }
+}
+
+func (s *SQL) Gather(acc telegraf.Accumulator) error {
+ var wg sync.WaitGroup
+
+ ctx, cancel := context.WithTimeout(context.Background(), time.Duration(s.Timeout))
+ defer cancel()
+
+ tstart := time.Now()
+ for _, query := range s.Queries {
+ wg.Add(1)
+
+ go func(q Query) {
+ defer wg.Done()
+ if err := s.executeQuery(ctx, acc, q, tstart); err != nil {
+ acc.AddError(err)
+ }
+ }(query)
+ }
+ wg.Wait()
+ s.Log.Debugf("Executed %d queries in %s", len(s.Queries), time.Since(tstart).String())
+
+ return nil
+}
+
+func init() {
+ inputs.Add("sql", func() telegraf.Input {
+ return &SQL{
+ MaxIdleTime: config.Duration(0), // unlimited
+ MaxLifetime: config.Duration(0), // unlimited
+ MaxOpenConnections: 0, // unlimited
+ MaxIdleConnections: magicIdleCount, // will trigger auto calculation
+ }
+ })
+}
+
+func (s *SQL) executeQuery(ctx context.Context, acc telegraf.Accumulator, q Query, tquery time.Time) error {
+ if q.statement == nil {
+ return fmt.Errorf("statement is nil for query %q", q.Query)
+ }
+
+ // Execute the query
+ rows, err := q.statement.QueryContext(ctx)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+
+ // Handle the rows
+ columnNames, err := rows.Columns()
+ if err != nil {
+ return err
+ }
+ rowCount, err := q.parse(ctx, acc, rows, tquery)
+ s.Log.Debugf("Received %d rows and %d columns for query %q", rowCount, len(columnNames), q.Query)
+
+ return err
+}
diff --git a/plugins/inputs/sql/sql_test.go b/plugins/inputs/sql/sql_test.go
new file mode 100644
index 000000000..35010eeb5
--- /dev/null
+++ b/plugins/inputs/sql/sql_test.go
@@ -0,0 +1,272 @@
+package sql
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "testing"
+ "time"
+
+ "math/rand"
+ "path/filepath"
+
+ "github.com/stretchr/testify/require"
+ "github.com/testcontainers/testcontainers-go"
+ "github.com/testcontainers/testcontainers-go/wait"
+
+ "github.com/influxdata/telegraf"
+ "github.com/influxdata/telegraf/testutil"
+)
+
+func pwgen(n int) string {
+ charset := []byte("abcdedfghijklmnopqrstABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
+
+ nchars := len(charset)
+ buffer := make([]byte, n)
+
+ for i := range buffer {
+ buffer[i] = charset[rand.Intn(nchars)]
+ }
+
+ return string(buffer)
+}
+
+var spinup = flag.Bool("spinup", false, "Spin-up the required test containers")
+
+func TestMariaDB(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping integration test in short mode")
+ }
+
+ logger := testutil.Logger{}
+
+ addr := "127.0.0.1"
+ port := "3306"
+ passwd := ""
+ database := "foo"
+
+ if *spinup {
+ logger.Infof("Spinning up container...")
+
+ // Generate a random password
+ passwd = pwgen(32)
+
+ // Determine the test-data mountpoint
+ testdata, err := filepath.Abs("testdata/mariadb")
+ require.NoError(t, err, "determining absolute path of test-data failed")
+
+ // Spin-up the container
+ ctx := context.Background()
+ req := testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{
+ Image: "mariadb",
+ Env: map[string]string{
+ "MYSQL_ROOT_PASSWORD": passwd,
+ "MYSQL_DATABASE": database,
+ },
+ BindMounts: map[string]string{
+ testdata: "/docker-entrypoint-initdb.d",
+ },
+ ExposedPorts: []string{"3306/tcp"},
+ WaitingFor: wait.ForListeningPort("3306/tcp"),
+ },
+ Started: true,
+ }
+ container, err := testcontainers.GenericContainer(ctx, req)
+ require.NoError(t, err, "starting container failed")
+ defer func() {
+ require.NoError(t, container.Terminate(ctx), "terminating container failed")
+ }()
+
+ // Get the connection details from the container
+ addr, err = container.Host(ctx)
+ require.NoError(t, err, "getting container host address failed")
+ p, err := container.MappedPort(ctx, "3306/tcp")
+ require.NoError(t, err, "getting container host port failed")
+ port = p.Port()
+ }
+
+ // Define the testset
+ var testset = []struct {
+ name string
+ queries []Query
+ expected []telegraf.Metric
+ }{
+ {
+ name: "metric_one",
+ queries: []Query{
+ {
+ Query: "SELECT * FROM metric_one",
+ TagColumnsInclude: []string{"tag_*"},
+ FieldColumnsExclude: []string{"tag_*", "timestamp"},
+ TimeColumn: "timestamp",
+ TimeFormat: "2006-01-02 15:04:05",
+ },
+ },
+ expected: []telegraf.Metric{
+ testutil.MustMetric(
+ "sql",
+ map[string]string{
+ "tag_one": "tag1",
+ "tag_two": "tag2",
+ },
+ map[string]interface{}{
+ "int64_one": int64(1234),
+ "int64_two": int64(2345),
+ },
+ time.Date(2021, 5, 17, 22, 4, 45, 0, time.UTC),
+ ),
+ },
+ },
+ }
+
+ for _, tt := range testset {
+ t.Run(tt.name, func(t *testing.T) {
+ // Setup the plugin-under-test
+ plugin := &SQL{
+ Driver: "maria",
+ Dsn: fmt.Sprintf("root:%s@tcp(%s:%s)/%s", passwd, addr, port, database),
+ Queries: tt.queries,
+ Log: logger,
+ }
+
+ var acc testutil.Accumulator
+
+ // Startup the plugin
+ err := plugin.Init()
+ require.NoError(t, err)
+ err = plugin.Start(&acc)
+ require.NoError(t, err)
+
+ // Gather
+ err = plugin.Gather(&acc)
+ require.NoError(t, err)
+ require.Len(t, acc.Errors, 0)
+
+ // Stopping the plugin
+ plugin.Stop()
+
+ // Do the comparison
+ testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics())
+ })
+ }
+}
+
+func TestPostgreSQL(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping integration test in short mode")
+ }
+
+ logger := testutil.Logger{}
+
+ addr := "127.0.0.1"
+ port := "5432"
+ passwd := ""
+ database := "foo"
+
+ if *spinup {
+ logger.Infof("Spinning up container...")
+
+ // Generate a random password
+ passwd = pwgen(32)
+
+ // Determine the test-data mountpoint
+ testdata, err := filepath.Abs("testdata/postgres")
+ require.NoError(t, err, "determining absolute path of test-data failed")
+
+ // Spin-up the container
+ ctx := context.Background()
+ req := testcontainers.GenericContainerRequest{
+ ContainerRequest: testcontainers.ContainerRequest{
+ Image: "postgres",
+ Env: map[string]string{
+ "POSTGRES_PASSWORD": passwd,
+ "POSTGRES_DB": database,
+ },
+ BindMounts: map[string]string{
+ testdata: "/docker-entrypoint-initdb.d",
+ },
+ ExposedPorts: []string{"5432/tcp"},
+ WaitingFor: wait.ForListeningPort("5432/tcp"),
+ },
+ Started: true,
+ }
+ container, err := testcontainers.GenericContainer(ctx, req)
+ require.NoError(t, err, "starting container failed")
+ defer func() {
+ require.NoError(t, container.Terminate(ctx), "terminating container failed")
+ }()
+
+ // Get the connection details from the container
+ addr, err = container.Host(ctx)
+ require.NoError(t, err, "getting container host address failed")
+ p, err := container.MappedPort(ctx, "5432/tcp")
+ require.NoError(t, err, "getting container host port failed")
+ port = p.Port()
+ }
+
+ // Define the testset
+ var testset = []struct {
+ name string
+ queries []Query
+ expected []telegraf.Metric
+ }{
+ {
+ name: "metric_one",
+ queries: []Query{
+ {
+ Query: "SELECT * FROM metric_one",
+ TagColumnsInclude: []string{"tag_*"},
+ FieldColumnsExclude: []string{"tag_*", "timestamp"},
+ TimeColumn: "timestamp",
+ TimeFormat: "2006-01-02 15:04:05",
+ },
+ },
+ expected: []telegraf.Metric{
+ testutil.MustMetric(
+ "sql",
+ map[string]string{
+ "tag_one": "tag1",
+ "tag_two": "tag2",
+ },
+ map[string]interface{}{
+ "int64_one": int64(1234),
+ "int64_two": int64(2345),
+ },
+ time.Date(2021, 5, 17, 22, 4, 45, 0, time.UTC),
+ ),
+ },
+ },
+ }
+
+ for _, tt := range testset {
+ t.Run(tt.name, func(t *testing.T) {
+ // Setup the plugin-under-test
+ plugin := &SQL{
+ Driver: "pgx",
+ Dsn: fmt.Sprintf("postgres://postgres:%v@%v:%v/%v", passwd, addr, port, database),
+ Queries: tt.queries,
+ Log: logger,
+ }
+
+ var acc testutil.Accumulator
+
+ // Startup the plugin
+ err := plugin.Init()
+ require.NoError(t, err)
+ err = plugin.Start(&acc)
+ require.NoError(t, err)
+
+ // Gather
+ err = plugin.Gather(&acc)
+ require.NoError(t, err)
+ require.Len(t, acc.Errors, 0)
+
+ // Stopping the plugin
+ plugin.Stop()
+
+ // Do the comparison
+ testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics())
+ })
+ }
+}
diff --git a/plugins/inputs/sql/testdata/mariadb/expected.sql b/plugins/inputs/sql/testdata/mariadb/expected.sql
new file mode 100644
index 000000000..49a3095db
--- /dev/null
+++ b/plugins/inputs/sql/testdata/mariadb/expected.sql
@@ -0,0 +1,36 @@
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `bar` (
+ `baz` int(11) DEFAULT NULL
+);
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `bar` VALUES (1);
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `metric three` (
+ `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
+ `tag four` text DEFAULT NULL,
+ `string two` text DEFAULT NULL
+);
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `metric three` VALUES ('2021-05-17 22:04:45','tag4','string2');
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `metric_one` (
+ `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
+ `tag_one` text DEFAULT NULL,
+ `tag_two` text DEFAULT NULL,
+ `int64_one` int(11) DEFAULT NULL,
+ `int64_two` int(11) DEFAULT NULL
+);
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `metric_one` VALUES ('2021-05-17 22:04:45','tag1','tag2',1234,2345);
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `metric_two` (
+ `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
+ `tag_three` text DEFAULT NULL,
+ `string_one` text DEFAULT NULL
+);
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `metric_two` VALUES ('2021-05-17 22:04:45','tag3','string1');
diff --git a/plugins/inputs/sql/testdata/postgres/expected.sql b/plugins/inputs/sql/testdata/postgres/expected.sql
new file mode 100644
index 000000000..8bc2b2fc8
--- /dev/null
+++ b/plugins/inputs/sql/testdata/postgres/expected.sql
@@ -0,0 +1,41 @@
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET idle_in_transaction_session_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SELECT pg_catalog.set_config('search_path', '', false);
+SET check_function_bodies = false;
+SET xmloption = content;
+SET client_min_messages = warning;
+SET row_security = off;
+SET default_tablespace = '';
+SET default_table_access_method = heap;
+CREATE TABLE public."metric three" (
+ "timestamp" timestamp without time zone,
+ "tag four" text,
+ "string two" text
+);
+ALTER TABLE public."metric three" OWNER TO postgres;
+CREATE TABLE public.metric_one (
+ "timestamp" timestamp without time zone,
+ tag_one text,
+ tag_two text,
+ int64_one integer,
+ int64_two integer
+);
+ALTER TABLE public.metric_one OWNER TO postgres;
+CREATE TABLE public.metric_two (
+ "timestamp" timestamp without time zone,
+ tag_three text,
+ string_one text
+);
+ALTER TABLE public.metric_two OWNER TO postgres;
+COPY public."metric three" ("timestamp", "tag four", "string two") FROM stdin;
+2021-05-17 22:04:45 tag4 string2
+\.
+COPY public.metric_one ("timestamp", tag_one, tag_two, int64_one, int64_two) FROM stdin;
+2021-05-17 22:04:45 tag1 tag2 1234 2345
+\.
+COPY public.metric_two ("timestamp", tag_three, string_one) FROM stdin;
+2021-05-17 22:04:45 tag3 string1
+\.