From d545b197ea2e309461146de0964916d52d6c7fdf Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Mon, 6 Jul 2015 01:46:43 -0300 Subject: [PATCH 01/11] Add docker containers to test services. This commit initializes the needed services which are not mocked so tests can be executed in any environment with docker. Some default modifications (i.e: connection strings) were also made to current tests to accomodate them for this setup. A docker-compose.yml file is provided with all the necessary parameters for this services to be initialized. Future services can be added easily by extending this configuration file In addition a makefile has been introduced to simplify command execution --- Makefile | 11 +++++++++++ README.md | 13 +++++++++++++ docker-compose.yml | 16 ++++++++++++++++ .../kafka_consumer_integration_test.go | 3 +++ plugins/mysql/mysql_test.go | 6 ++++-- plugins/postgresql/postgresql_test.go | 6 +++--- plugins/redis/redis_test.go | 8 ++++---- 7 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 Makefile create mode 100644 docker-compose.yml diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..12b3aeac7 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +prepare: + go get -d -v -t ./... + docker-compose up -d + +test: prepare + go test -short ./... + +update: + go get -u -v -d -t ./... + +.PHONY: test diff --git a/README.md b/README.md index e7e95455d..c3373f4b8 100644 --- a/README.md +++ b/README.md @@ -175,3 +175,16 @@ func init() { } ``` +## Testing + +As Telegraf collects metrics from several third-party services it becomes a difficult task to mock each service as +some of them have complicated protocols which would take some time to replicate. + +To overcome this situation we've decided to use docker containers to provide a fast and reproducible environment +to test those services which require it. For other situations (i.e: https://github.com/influxdb/telegraf/blob/master/plugins/redis/redis_test.go ) a simple mock will suffice. + +To execute Telegraf tests follow this simple steps: + +- Install docker compose following [these](https://docs.docker.com/compose/install/) instructions +- execute `make test` + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..d54a25f75 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +mysql: + image: mysql + ports: + - "3306:3306" + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: true + +memcached: + image: memcached + ports: + - "11211:11211" + +postgres: + image: postgres + ports: + - "5432:5432" diff --git a/plugins/kafka_consumer/kafka_consumer_integration_test.go b/plugins/kafka_consumer/kafka_consumer_integration_test.go index 1541cb127..2a82a2374 100644 --- a/plugins/kafka_consumer/kafka_consumer_integration_test.go +++ b/plugins/kafka_consumer/kafka_consumer_integration_test.go @@ -14,6 +14,9 @@ import ( ) func TestReadsMetricsFromKafka(t *testing.T) { + if testing.Short() { + t.Skip("Skipping integration test in short mode") + } var zkPeers, brokerPeers []string if len(os.Getenv("ZOOKEEPER_PEERS")) == 0 { diff --git a/plugins/mysql/mysql_test.go b/plugins/mysql/mysql_test.go index 76cb28b0d..f09c0307e 100644 --- a/plugins/mysql/mysql_test.go +++ b/plugins/mysql/mysql_test.go @@ -11,7 +11,7 @@ import ( func TestMysqlGeneratesMetrics(t *testing.T) { m := &Mysql{ - Servers: []string{""}, + Servers: []string{"root@tcp(localhost:3306)/"}, } var acc testutil.Accumulator @@ -53,7 +53,9 @@ func TestMysqlGeneratesMetrics(t *testing.T) { } func TestMysqlDefaultsToLocal(t *testing.T) { - m := &Mysql{} + m := &Mysql{ + Servers: []string{"root@tcp(localhost:3306)/"}, + } var acc testutil.Accumulator diff --git a/plugins/postgresql/postgresql_test.go b/plugins/postgresql/postgresql_test.go index 91776d619..775087700 100644 --- a/plugins/postgresql/postgresql_test.go +++ b/plugins/postgresql/postgresql_test.go @@ -12,7 +12,7 @@ func TestPostgresqlGeneratesMetrics(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "sslmode=disable", + Address: "host=localhost user=postgres sslmode=disable", Databases: []string{"postgres"}, }, }, @@ -57,7 +57,7 @@ func TestPostgresqlTagsMetricsWithDatabaseName(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "sslmode=disable", + Address: "host=localhost user=postgres sslmode=disable", Databases: []string{"postgres"}, }, }, @@ -78,7 +78,7 @@ func TestPostgresqlDefaultsToAllDatabases(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "sslmode=disable", + Address: "host=localhost user=postgres sslmode=disable", }, }, } diff --git a/plugins/redis/redis_test.go b/plugins/redis/redis_test.go index 317fde783..7bd412e15 100644 --- a/plugins/redis/redis_test.go +++ b/plugins/redis/redis_test.go @@ -83,7 +83,7 @@ func TestRedisGeneratesMetrics(t *testing.T) { } for _, c := range checkInt { - assert.NoError(t, acc.ValidateValue(c.name, c.value)) + assert.True(t, acc.CheckValue(c.name, c.value)) } checkFloat := []struct { @@ -98,7 +98,7 @@ func TestRedisGeneratesMetrics(t *testing.T) { } for _, c := range checkFloat { - assert.NoError(t, acc.ValidateValue(c.name, c.value)) + assert.True(t, acc.CheckValue(c.name, c.value)) } } @@ -174,7 +174,7 @@ func TestRedisCanPullStatsFromMultipleServers(t *testing.T) { } for _, c := range checkInt { - assert.NoError(t, acc.ValidateValue(c.name, c.value)) + assert.True(t, acc.CheckValue(c.name, c.value)) } checkFloat := []struct { @@ -189,7 +189,7 @@ func TestRedisCanPullStatsFromMultipleServers(t *testing.T) { } for _, c := range checkFloat { - assert.NoError(t, acc.ValidateValue(c.name, c.value)) + assert.True(t, acc.CheckValue(c.name, c.value)) } } From 0db55007abc2a7d2b8acba98f1b7bdb8735f711a Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Mon, 6 Jul 2015 01:51:13 -0300 Subject: [PATCH 02/11] Add cirleci script --- circle.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..0c76101b9 --- /dev/null +++ b/circle.yml @@ -0,0 +1,15 @@ +machine: + python: + version: 2.7.3 + services: + - docker + +dependencies: + pre: + - pip install -U docker-compose==1.2.0 + - docker-compose pull + + +test: + override: + - make test From ae385b336d081b84f5d4731b3e29a3e108db81a8 Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Mon, 6 Jul 2015 02:20:25 -0300 Subject: [PATCH 03/11] Remove unnecessary circleci configuration as we're using default provided services Update test users to use circleci default services --- circle.yml | 14 +------------- docker-compose.yml | 4 +++- plugins/postgresql/postgresql_test.go | 6 +++--- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/circle.yml b/circle.yml index 0c76101b9..c5cfc7323 100644 --- a/circle.yml +++ b/circle.yml @@ -1,15 +1,3 @@ -machine: - python: - version: 2.7.3 - services: - - docker - -dependencies: - pre: - - pip install -U docker-compose==1.2.0 - - docker-compose pull - - test: override: - - make test + - go test -short ./... diff --git a/docker-compose.yml b/docker-compose.yml index d54a25f75..5945f06ab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ mysql: ports: - "3306:3306" environment: - MYSQL_ALLOW_EMPTY_PASSWORD: true + MYSQL_ALLOW_EMPTY_PASSWORD: yes memcached: image: memcached @@ -14,3 +14,5 @@ postgres: image: postgres ports: - "5432:5432" + environment: + POSTGRES_USER: ubuntu diff --git a/plugins/postgresql/postgresql_test.go b/plugins/postgresql/postgresql_test.go index 775087700..ab17601b1 100644 --- a/plugins/postgresql/postgresql_test.go +++ b/plugins/postgresql/postgresql_test.go @@ -12,7 +12,7 @@ func TestPostgresqlGeneratesMetrics(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "host=localhost user=postgres sslmode=disable", + Address: "host=localhost user=ubuntu sslmode=disable", Databases: []string{"postgres"}, }, }, @@ -57,7 +57,7 @@ func TestPostgresqlTagsMetricsWithDatabaseName(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "host=localhost user=postgres sslmode=disable", + Address: "host=localhost user=ubuntu sslmode=disable", Databases: []string{"postgres"}, }, }, @@ -78,7 +78,7 @@ func TestPostgresqlDefaultsToAllDatabases(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "host=localhost user=postgres sslmode=disable", + Address: "host=localhost user=ubuntu sslmode=disable", }, }, } From 63552282d70eb1e802812132ffce4c8bf4041d58 Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Mon, 6 Jul 2015 03:38:08 -0300 Subject: [PATCH 04/11] Remove circle ci implementation due to Golang bug. I've tried to set-up circleci but I came across a Golang issue that was preventing CPU tests to PASS. Instead of ignoring those tests I've submitted an issue to Go (https://github.com/golang/go/issues/11609) hoping that this gets fixed soon. --- circle.yml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 circle.yml diff --git a/circle.yml b/circle.yml deleted file mode 100644 index c5cfc7323..000000000 --- a/circle.yml +++ /dev/null @@ -1,3 +0,0 @@ -test: - override: - - go test -short ./... From 4471e2bdbbe42c6baf887c27300d2d26206648bc Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Mon, 6 Jul 2015 03:46:53 -0300 Subject: [PATCH 05/11] Use postgres default configuration --- docker-compose.yml | 2 -- plugins/postgresql/postgresql_test.go | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5945f06ab..f2c1b2b54 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,5 +14,3 @@ postgres: image: postgres ports: - "5432:5432" - environment: - POSTGRES_USER: ubuntu diff --git a/plugins/postgresql/postgresql_test.go b/plugins/postgresql/postgresql_test.go index ab17601b1..775087700 100644 --- a/plugins/postgresql/postgresql_test.go +++ b/plugins/postgresql/postgresql_test.go @@ -12,7 +12,7 @@ func TestPostgresqlGeneratesMetrics(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "host=localhost user=ubuntu sslmode=disable", + Address: "host=localhost user=postgres sslmode=disable", Databases: []string{"postgres"}, }, }, @@ -57,7 +57,7 @@ func TestPostgresqlTagsMetricsWithDatabaseName(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "host=localhost user=ubuntu sslmode=disable", + Address: "host=localhost user=postgres sslmode=disable", Databases: []string{"postgres"}, }, }, @@ -78,7 +78,7 @@ func TestPostgresqlDefaultsToAllDatabases(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "host=localhost user=ubuntu sslmode=disable", + Address: "host=localhost user=postgres sslmode=disable", }, }, } From aa86c16838acc707fcad583701677def8a6ef704 Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Mon, 6 Jul 2015 21:17:44 -0300 Subject: [PATCH 06/11] Add --no-recreate option to prepare target --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 12b3aeac7..cefa9ac44 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ prepare: go get -d -v -t ./... - docker-compose up -d + docker-compose up -d --no-recreate test: prepare go test -short ./... From d2810ddc95b5400ef26a733d5fbe4a0a1f0acaea Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Mon, 6 Jul 2015 22:18:31 -0300 Subject: [PATCH 07/11] Add DOCKER_HOST support for tests This allows to run tests in environments where DOCKER_HOST is used. This is extremely helpful when using boot2docker to run docker --- plugins/memcached/memcached_test.go | 2 +- plugins/mysql/mysql_test.go | 5 +++-- plugins/postgresql/postgresql_test.go | 7 ++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/memcached/memcached_test.go b/plugins/memcached/memcached_test.go index 08e696fb7..522fc7bc7 100644 --- a/plugins/memcached/memcached_test.go +++ b/plugins/memcached/memcached_test.go @@ -10,7 +10,7 @@ import ( func TestMemcachedGeneratesMetrics(t *testing.T) { m := &Memcached{ - Servers: []string{"localhost"}, + Servers: []string{testutil.GetLocalHost()}, } var acc testutil.Accumulator diff --git a/plugins/mysql/mysql_test.go b/plugins/mysql/mysql_test.go index f09c0307e..e29e2e12e 100644 --- a/plugins/mysql/mysql_test.go +++ b/plugins/mysql/mysql_test.go @@ -1,6 +1,7 @@ package mysql import ( + "fmt" "strings" "testing" @@ -11,7 +12,7 @@ import ( func TestMysqlGeneratesMetrics(t *testing.T) { m := &Mysql{ - Servers: []string{"root@tcp(localhost:3306)/"}, + Servers: []string{fmt.Sprintf("root@tcp(%s:3306)/", testutil.GetLocalHost())}, } var acc testutil.Accumulator @@ -54,7 +55,7 @@ func TestMysqlGeneratesMetrics(t *testing.T) { func TestMysqlDefaultsToLocal(t *testing.T) { m := &Mysql{ - Servers: []string{"root@tcp(localhost:3306)/"}, + Servers: []string{fmt.Sprintf("root@tcp(%s:3306)/", testutil.GetLocalHost())}, } var acc testutil.Accumulator diff --git a/plugins/postgresql/postgresql_test.go b/plugins/postgresql/postgresql_test.go index 775087700..f9fb2b98e 100644 --- a/plugins/postgresql/postgresql_test.go +++ b/plugins/postgresql/postgresql_test.go @@ -1,6 +1,7 @@ package postgresql import ( + "fmt" "testing" "github.com/influxdb/telegraf/testutil" @@ -12,7 +13,7 @@ func TestPostgresqlGeneratesMetrics(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "host=localhost user=postgres sslmode=disable", + Address: fmt.Sprintf("host=%s user=postgres sslmode=disable", testutil.GetLocalHost()), Databases: []string{"postgres"}, }, }, @@ -57,7 +58,7 @@ func TestPostgresqlTagsMetricsWithDatabaseName(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "host=localhost user=postgres sslmode=disable", + Address: fmt.Sprintf("host=%s user=postgres sslmode=disable", testutil.GetLocalHost()), Databases: []string{"postgres"}, }, }, @@ -78,7 +79,7 @@ func TestPostgresqlDefaultsToAllDatabases(t *testing.T) { p := &Postgresql{ Servers: []*Server{ { - Address: "host=localhost user=postgres sslmode=disable", + Address: fmt.Sprintf("host=%s user=postgres sslmode=disable", testutil.GetLocalHost()), }, }, } From ef335d9fd736923fef0365cab7814be15eb338cd Mon Sep 17 00:00:00 2001 From: Marcos Lilljedahl Date: Mon, 6 Jul 2015 22:21:46 -0300 Subject: [PATCH 08/11] Add missing files --- testutil/testutil.go | 12 ++++++++++++ testutil/testutil_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 testutil/testutil.go create mode 100644 testutil/testutil_test.go diff --git a/testutil/testutil.go b/testutil/testutil.go new file mode 100644 index 000000000..71c3ce0c2 --- /dev/null +++ b/testutil/testutil.go @@ -0,0 +1,12 @@ +package testutil + +import "os" + +var localhost = "localhost" + +func GetLocalHost() string { + if dockerHostVar := os.Getenv("DOCKER_HOST"); dockerHostVar != "" { + return dockerHostVar + } + return localhost +} diff --git a/testutil/testutil_test.go b/testutil/testutil_test.go new file mode 100644 index 000000000..39f548c0f --- /dev/null +++ b/testutil/testutil_test.go @@ -0,0 +1,26 @@ +package testutil + +import ( + "os" + "testing" +) + +func TestDockerHost(t *testing.T) { + + os.Unsetenv("DOCKER_HOST") + + host := GetLocalHost() + + if host != localhost { + t.Fatalf("Host should be localhost when DOCKER_HOST is not set. Current value [%s]", host) + } + + os.Setenv("DOCKER_HOST", "1.1.1.1") + + host = GetLocalHost() + + if host != "1.1.1.1" { + t.Fatalf("Host should take DOCKER_HOST value when set. Current value is [%s] and DOCKER_HOST is [%s]", host, os.Getenv("DOCKER_HOST")) + } + +} From 03c520798e37b694d2e339d3919d202513ca471d Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Mon, 3 Aug 2015 17:16:02 -0600 Subject: [PATCH 09/11] Fix GetLocalHost testutil function for mac users (boot2docker) --- testutil/testutil.go | 19 +++++++++++++++++-- testutil/testutil_test.go | 8 ++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/testutil/testutil.go b/testutil/testutil.go index 71c3ce0c2..8735e882d 100644 --- a/testutil/testutil.go +++ b/testutil/testutil.go @@ -1,12 +1,27 @@ package testutil -import "os" +import ( + "net" + "net/url" + "os" +) var localhost = "localhost" func GetLocalHost() string { if dockerHostVar := os.Getenv("DOCKER_HOST"); dockerHostVar != "" { - return dockerHostVar + u, err := url.Parse(dockerHostVar) + if err != nil { + return dockerHostVar + } + + // split out the ip addr from the port + host, _, err := net.SplitHostPort(u.Host) + if err != nil { + return dockerHostVar + } + + return host } return localhost } diff --git a/testutil/testutil_test.go b/testutil/testutil_test.go index 39f548c0f..52a807514 100644 --- a/testutil/testutil_test.go +++ b/testutil/testutil_test.go @@ -23,4 +23,12 @@ func TestDockerHost(t *testing.T) { t.Fatalf("Host should take DOCKER_HOST value when set. Current value is [%s] and DOCKER_HOST is [%s]", host, os.Getenv("DOCKER_HOST")) } + os.Setenv("DOCKER_HOST", "tcp://1.1.1.1:8080") + + host = GetLocalHost() + + if host != "1.1.1.1" { + t.Fatalf("Host should take DOCKER_HOST value when set. Current value is [%s] and DOCKER_HOST is [%s]", host, os.Getenv("DOCKER_HOST")) + } + } From 85ecee35256f0763f4282d2ee50dba2a03245279 Mon Sep 17 00:00:00 2001 From: Simon Fraser Date: Tue, 4 Aug 2015 15:30:27 +0100 Subject: [PATCH 10/11] Add default log rotation --- package.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/package.sh b/package.sh index c20e566cb..fca719e8c 100755 --- a/package.sh +++ b/package.sh @@ -35,8 +35,10 @@ AWS_FILE=~/aws.conf INSTALL_ROOT_DIR=/opt/telegraf TELEGRAF_LOG_DIR=/var/log/telegraf CONFIG_ROOT_DIR=/etc/opt/telegraf +LOGROTATE_DIR=/etc/logrotate.d SAMPLE_CONFIGURATION=etc/config.sample.toml +LOGROTATE_CONFIGURATION=etc/logrotate.d/telegraf INITD_SCRIPT=scripts/init.sh TMP_WORK_DIR=`mktemp -d` @@ -144,6 +146,11 @@ make_dir_tree() { echo "Failed to create configuration directory -- aborting." cleanup_exit 1 fi + mkdir -p $work_dir/$LOGROTATE_DIR + if [ $? -ne 0 ]; then + echo "Failed to create configuration directory -- aborting." + cleanup_exit 1 + fi } @@ -251,6 +258,12 @@ if [ $? -ne 0 ]; then cleanup_exit 1 fi +cp $LOGROTATE_CONFIGURATION $TMP_WORK_DIR/$LOGROTATE_DIR/telegraf.conf +if [ $? -ne 0 ]; then + echo "Failed to copy $LOGROTATE_CONFIGURATION to packaging directory -- aborting." + cleanup_exit 1 +fi + generate_postinstall_script $VERSION ########################################################################### From 3ff2ea8d4e84ea25f1874bd050fc054f60a88c7d Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Tue, 4 Aug 2015 08:58:32 -0600 Subject: [PATCH 11/11] Creating circleci job to just lint and vet code --- accumulator.go | 4 ++++ agent.go | 21 ++++++++++++++++---- circle.yml | 17 ++++++++++++++++ cmd/telegraf/telegraf.go | 2 ++ config.go | 26 +++++++++++++++++++++---- plugins/mongodb/mongostat.go | 2 +- plugins/rethinkdb/rethinkdb_server.go | 4 ++-- plugins/system/ps/disk/disk_test.go | 2 +- testutil/accumulator.go | 28 ++++++++++++++++++++++++--- testutil/testutil.go | 2 ++ 10 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 circle.yml diff --git a/accumulator.go b/accumulator.go index ab5a02dae..b3f7a4511 100644 --- a/accumulator.go +++ b/accumulator.go @@ -10,6 +10,8 @@ import ( "github.com/influxdb/influxdb/client" ) +// BatchPoints is used to send a batch of data in a single write from telegraf +// to influx type BatchPoints struct { mu sync.Mutex @@ -22,6 +24,7 @@ type BatchPoints struct { Config *ConfiguredPlugin } +// Add adds a measurement func (bp *BatchPoints) Add(measurement string, val interface{}, tags map[string]string) { bp.mu.Lock() defer bp.mu.Unlock() @@ -55,6 +58,7 @@ func (bp *BatchPoints) Add(measurement string, val interface{}, tags map[string] }) } +// AddValuesWithTime adds a measurement with a provided timestamp func (bp *BatchPoints) AddValuesWithTime( measurement string, values map[string]interface{}, diff --git a/agent.go b/agent.go index 7f45e47a2..7e47ff21a 100644 --- a/agent.go +++ b/agent.go @@ -19,8 +19,13 @@ type runningPlugin struct { config *ConfiguredPlugin } +// Agent runs telegraf and collects data based on the given config type Agent struct { + + // Interval at which to gather information Interval Duration + + // Run in debug mode? Debug bool Hostname string @@ -31,6 +36,7 @@ type Agent struct { conn *client.Client } +// NewAgent returns an Agent struct based off the given Config func NewAgent(config *Config) (*Agent, error) { agent := &Agent{Config: config, Interval: Duration{10 * time.Second}} @@ -57,8 +63,9 @@ func NewAgent(config *Config) (*Agent, error) { return agent, nil } -func (agent *Agent) Connect() error { - config := agent.Config +// Connect connects to the agent's config URL +func (a *Agent) Connect() error { + config := a.Config u, err := url.Parse(config.URL) if err != nil { @@ -77,11 +84,12 @@ func (agent *Agent) Connect() error { return err } - agent.conn = c + a.conn = c return nil } +// LoadPlugins loads the agent's plugins func (a *Agent) LoadPlugins() ([]string, error) { var names []string @@ -201,10 +209,12 @@ func (a *Agent) crankSeparate(shutdown chan struct{}, plugin *runningPlugin) err } } +// TestAllPlugins verifies that we can 'Gather' from all plugins with the +// default configuration func (a *Agent) TestAllPlugins() error { var names []string - for name, _ := range plugins.Plugins { + for name := range plugins.Plugins { names = append(names, name) } @@ -230,6 +240,8 @@ func (a *Agent) TestAllPlugins() error { return nil } +// Test verifies that we can 'Gather' from all plugins with their configured +// Config struct func (a *Agent) Test() error { var acc BatchPoints @@ -253,6 +265,7 @@ func (a *Agent) Test() error { return nil } +// Run runs the agent daemon, gathering every Interval func (a *Agent) Run(shutdown chan struct{}) error { if a.conn == nil { err := a.Connect() diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..6c346a360 --- /dev/null +++ b/circle.yml @@ -0,0 +1,17 @@ +dependencies: + post: + # install golint + - go get github.com/golang/lint/golint + # install binaries + - go install ./... + +test: + pre: + # Vet go code for any potential errors + - go vet ./... + override: + # Enforce that testutil, cmd, and main directory are fully linted + - golint . + - golint testutil/... + - golint cmd/... + # TODO run unit tests diff --git a/cmd/telegraf/telegraf.go b/cmd/telegraf/telegraf.go index f36693ebc..04a292d2b 100644 --- a/cmd/telegraf/telegraf.go +++ b/cmd/telegraf/telegraf.go @@ -19,7 +19,9 @@ var fVersion = flag.Bool("version", false, "display the version") var fSampleConfig = flag.Bool("sample-config", false, "print out full sample configuration") var fPidfile = flag.String("pidfile", "", "file to write our pid to") +// Telegraf version var Version = "unreleased" +// Telegraf commit var Commit = "" func main() { diff --git a/config.go b/config.go index a94c1c490..4382fc7c0 100644 --- a/config.go +++ b/config.go @@ -13,10 +13,12 @@ import ( "github.com/naoina/toml/ast" ) +// Duration just wraps time.Duration type Duration struct { time.Duration } +// UnmarshalTOML parses the duration from the TOML config file func (d *Duration) UnmarshalTOML(b []byte) error { dur, err := time.ParseDuration(string(b[1 : len(b)-1])) if err != nil { @@ -28,6 +30,9 @@ func (d *Duration) UnmarshalTOML(b []byte) error { return nil } +// Config specifies the URL/user/password for the database that telegraf +// will be logging to, as well as all the plugins that the user has +// specified type Config struct { URL string Username string @@ -41,10 +46,12 @@ type Config struct { plugins map[string]*ast.Table } +// Plugins returns the configured plugins as a map of name -> plugin toml func (c *Config) Plugins() map[string]*ast.Table { return c.plugins } +// ConfiguredPlugin containing a name, interval, and drop/pass prefix lists type ConfiguredPlugin struct { Name string @@ -54,6 +61,7 @@ type ConfiguredPlugin struct { Interval time.Duration } +// ShouldPass returns true if the metric should pass, false if should drop func (cp *ConfiguredPlugin) ShouldPass(measurement string) bool { if cp.Pass != nil { for _, pat := range cp.Pass { @@ -78,6 +86,7 @@ func (cp *ConfiguredPlugin) ShouldPass(measurement string) bool { return true } +// ApplyAgent loads the toml config into the given interface func (c *Config) ApplyAgent(v interface{}) error { if c.agent != nil { return toml.UnmarshalTable(c.agent, v) @@ -86,6 +95,9 @@ func (c *Config) ApplyAgent(v interface{}) error { return nil } +// ApplyPlugin takes defined plugin names and applies them to the given +// interface, returning a ConfiguredPlugin object in the end that can +// be inserted into a runningPlugin by the agent. func (c *Config) ApplyPlugin(name string, v interface{}) (*ConfiguredPlugin, error) { cp := &ConfiguredPlugin{Name: name} @@ -137,10 +149,11 @@ func (c *Config) ApplyPlugin(name string, v interface{}) (*ConfiguredPlugin, err return cp, nil } +// PluginsDeclared returns the name of all plugins declared in the config. func (c *Config) PluginsDeclared() []string { var plugins []string - for name, _ := range c.plugins { + for name := range c.plugins { plugins = append(plugins, name) } @@ -149,12 +162,14 @@ func (c *Config) PluginsDeclared() []string { return plugins } +// DefaultConfig returns an empty default configuration func DefaultConfig() *Config { return &Config{} } -var ErrInvalidConfig = errors.New("invalid configuration") +var errInvalidConfig = errors.New("invalid configuration") +// LoadConfig loads the given config file and returns a *Config pointer func LoadConfig(path string) (*Config, error) { data, err := ioutil.ReadFile(path) if err != nil { @@ -173,7 +188,7 @@ func LoadConfig(path string) (*Config, error) { for name, val := range tbl.Fields { subtbl, ok := val.(*ast.Table) if !ok { - return nil, ErrInvalidConfig + return nil, errInvalidConfig } switch name { @@ -192,6 +207,8 @@ func LoadConfig(path string) (*Config, error) { return c, nil } +// ListTags returns a string of tags specified in the config, +// line-protocol style func (c *Config) ListTags() string { var tags []string @@ -271,12 +288,13 @@ database = "telegraf" # required. ` +// PrintSampleConfig prints the sample config! func PrintSampleConfig() { fmt.Printf(header) var names []string - for name, _ := range plugins.Plugins { + for name := range plugins.Plugins { names = append(names, name) } diff --git a/plugins/mongodb/mongostat.go b/plugins/mongodb/mongostat.go index ae39a3e3a..b3c990b1a 100644 --- a/plugins/mongodb/mongostat.go +++ b/plugins/mongodb/mongostat.go @@ -457,7 +457,7 @@ func NewStatLine(oldStat, newStat ServerStatus, key string, all bool, sampleSecs oldStat.ExtraInfo.PageFaults != nil && newStat.ExtraInfo.PageFaults != nil { returnVal.Faults = diff(*(newStat.ExtraInfo.PageFaults), *(oldStat.ExtraInfo.PageFaults), sampleSecs) } - if !returnVal.IsMongos && oldStat.Locks != nil && oldStat.Locks != nil { + if !returnVal.IsMongos && oldStat.Locks != nil { globalCheck, hasGlobal := oldStat.Locks["Global"] if hasGlobal && globalCheck.AcquireCount != nil { // This appears to be a 3.0+ server so the data in these fields do *not* refer to diff --git a/plugins/rethinkdb/rethinkdb_server.go b/plugins/rethinkdb/rethinkdb_server.go index 43551fe25..9285068bd 100644 --- a/plugins/rethinkdb/rethinkdb_server.go +++ b/plugins/rethinkdb/rethinkdb_server.go @@ -118,7 +118,7 @@ func (s *Server) addClusterStats(acc plugins.Accumulator) error { defer cursor.Close() var clusterStats stats if err := cursor.One(&clusterStats); err != nil { - return fmt.Errorf("failure to parse cluster stats, $s\n", err.Error()) + return fmt.Errorf("failure to parse cluster stats, %s\n", err.Error()) } tags := s.getDefaultTags() @@ -146,7 +146,7 @@ func (s *Server) addMemberStats(acc plugins.Accumulator) error { defer cursor.Close() var memberStats stats if err := cursor.One(&memberStats); err != nil { - return fmt.Errorf("failure to parse member stats, $s\n", err.Error()) + return fmt.Errorf("failure to parse member stats, %s\n", err.Error()) } tags := s.getDefaultTags() diff --git a/plugins/system/ps/disk/disk_test.go b/plugins/system/ps/disk/disk_test.go index 04776b1d8..6a91bae8c 100644 --- a/plugins/system/ps/disk/disk_test.go +++ b/plugins/system/ps/disk/disk_test.go @@ -45,7 +45,7 @@ func TestDisk_io_counters(t *testing.T) { t.Errorf("error %v", err) } if len(ret) == 0 { - t.Errorf("ret is empty", ret) + t.Errorf("ret is empty: %s", ret) } empty := DiskIOCountersStat{} for part, io := range ret { diff --git a/testutil/accumulator.go b/testutil/accumulator.go index 56657e711..238eb5042 100644 --- a/testutil/accumulator.go +++ b/testutil/accumulator.go @@ -6,6 +6,7 @@ import ( "time" ) +// Point defines a single point measurement type Point struct { Measurement string Tags map[string]string @@ -13,10 +14,12 @@ type Point struct { Time time.Time } +// Accumulator defines a mocked out accumulator type Accumulator struct { Points []*Point } +// Add adds a measurement point to the accumulator func (a *Accumulator) Add(measurement string, value interface{}, tags map[string]string) { if tags == nil { tags = map[string]string{} @@ -31,6 +34,7 @@ func (a *Accumulator) Add(measurement string, value interface{}, tags map[string ) } +// AddValuesWithTime adds a measurement point with a specified timestamp. func (a *Accumulator) AddValuesWithTime( measurement string, values map[string]interface{}, @@ -48,6 +52,7 @@ func (a *Accumulator) AddValuesWithTime( ) } +// Get gets the specified measurement point from the accumulator func (a *Accumulator) Get(measurement string) (*Point, bool) { for _, p := range a.Points { if p.Measurement == measurement { @@ -58,6 +63,8 @@ func (a *Accumulator) Get(measurement string) (*Point, bool) { return nil, false } +// CheckValue checks that the accumulators point for the given measurement +// is the same as the given value. func (a *Accumulator) CheckValue(measurement string, val interface{}) bool { for _, p := range a.Points { if p.Measurement == measurement { @@ -68,11 +75,22 @@ func (a *Accumulator) CheckValue(measurement string, val interface{}) bool { return false } -func (a *Accumulator) CheckTaggedValue(measurement string, val interface{}, tags map[string]string) bool { +// CheckTaggedValue calls ValidateTaggedValue +func (a *Accumulator) CheckTaggedValue( + measurement string, + val interface{}, + tags map[string]string, +) bool { return a.ValidateTaggedValue(measurement, val, tags) == nil } -func (a *Accumulator) ValidateTaggedValue(measurement string, val interface{}, tags map[string]string) error { +// ValidateTaggedValue validates that the given measurement and value exist +// in the accumulator and with the given tags. +func (a *Accumulator) ValidateTaggedValue( + measurement string, + val interface{}, + tags map[string]string, +) error { if tags == nil { tags = map[string]string{} } @@ -83,7 +101,8 @@ func (a *Accumulator) ValidateTaggedValue(measurement string, val interface{}, t if p.Measurement == measurement { if p.Values["value"] != val { - return fmt.Errorf("%v (%T) != %v (%T)", p.Values["value"], p.Values["value"], val, val) + return fmt.Errorf("%v (%T) != %v (%T)", + p.Values["value"], p.Values["value"], val, val) } return nil } @@ -92,10 +111,12 @@ func (a *Accumulator) ValidateTaggedValue(measurement string, val interface{}, t return fmt.Errorf("unknown measurement %s with tags %v", measurement, tags) } +// ValidateValue calls ValidateTaggedValue func (a *Accumulator) ValidateValue(measurement string, val interface{}) error { return a.ValidateTaggedValue(measurement, val, nil) } +// HasIntValue returns true if the measurement has an Int value func (a *Accumulator) HasIntValue(measurement string) bool { for _, p := range a.Points { if p.Measurement == measurement { @@ -107,6 +128,7 @@ func (a *Accumulator) HasIntValue(measurement string) bool { return false } +// HasFloatValue returns true if the given measurement has a float value func (a *Accumulator) HasFloatValue(measurement string) bool { for _, p := range a.Points { if p.Measurement == measurement { diff --git a/testutil/testutil.go b/testutil/testutil.go index 8735e882d..91eb4b6b9 100644 --- a/testutil/testutil.go +++ b/testutil/testutil.go @@ -8,6 +8,8 @@ import ( var localhost = "localhost" +// GetLocalHost returns the DOCKER_HOST environment variable, parsing +// out any scheme or ports so that only the IP address is returned. func GetLocalHost() string { if dockerHostVar := os.Getenv("DOCKER_HOST"); dockerHostVar != "" { u, err := url.Parse(dockerHostVar)