0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-13 09:11:50 +00:00

go.d rename example => testrandom ()

This commit is contained in:
Ilya Mashchenko 2024-09-16 12:57:26 +03:00 committed by GitHub
parent 39a3bff6d1
commit 0320acd537
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 158 additions and 290 deletions

View file

@ -76,7 +76,6 @@ see the appropriate collector readme.
| [dovecot](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/dovecot) | Dovecot |
| [elasticsearch](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/elasticsearch) | Elasticsearch/OpenSearch |
| [envoy](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/envoy) | Envoy |
| [example](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/example) | - |
| [exim](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/exim) | Exim |
| [fail2ban](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/fail2ban) | Fail2Ban Jails |
| [filecheck](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/filecheck) | Files and Directories |

View file

@ -41,7 +41,6 @@ modules:
# dovecot: yes
# elasticsearch: yes
# envoy: yes
# example: no
# exim: yes
# fail2ban: yes
# filecheck: yes

View file

@ -1,5 +0,0 @@
## All available configuration options, their descriptions and default values:
## https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/example#readme
jobs:
- name: example

View file

@ -1,80 +0,0 @@
<!--
title: "Example module"
description: "Use this example data collection module, which produces example charts with random values, to better understand how to build your own collector in Go."
custom_edit_url: "https://github.com/netdata/go.d.plugin/edit/master/modules/example/README.md"
sidebar_label: "Example module in Go"
learn_status: "Published"
learn_topic_type: "References"
learn_rel_path: "Integrations/Monitor/Mock Collectors"
-->
# Example module
An example data collection module. Use it as an example writing a new module.
## Charts
This module produces example charts with random values. Number of charts, dimensions and chart type is configurable.
## Configuration
Edit the `go.d/example.conf` configuration file using `edit-config` from the
Netdata [config directory](/docs/netdata-agent/configuration/README.md), which is typically at `/etc/netdata`.
```bash
cd /etc/netdata # Replace this path with your Netdata config directory
sudo ./edit-config go.d/example.conf
```
Disabled by default. Should be explicitly enabled
in [go.d.conf](https://github.com/netdata/netdata/blob/master/src/go/plugin/go.d/config/go.d.conf).
```yaml
# go.d.conf
modules:
example: yes
```
Here is an example configuration with several jobs:
```yaml
jobs:
- name: example
charts:
num: 3
dimensions: 5
- name: hidden_example
hidden_charts:
num: 3
dimensions: 5
```
---
For all available options, see the Example
collector's [configuration file](https://github.com/netdata/netdata/blob/master/src/go/plugin/go.d/config/go.d/example.conf).
## Troubleshooting
To troubleshoot issues with the `example` collector, run the `go.d.plugin` with the debug option enabled. The output
should give you clues as to why the collector isn't working.
- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on
your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`.
```bash
cd /usr/libexec/netdata/plugins.d/
```
- Switch to the `netdata` user.
```bash
sudo -u netdata -s
```
- Run the `go.d.plugin` to debug the collector:
```bash
./go.d.plugin -d -m example
```

View file

@ -1,64 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package example
import (
"errors"
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
)
func (e *Example) validateConfig() error {
if e.Config.Charts.Num <= 0 && e.Config.HiddenCharts.Num <= 0 {
return errors.New("'charts->num' or `hidden_charts->num` must be > 0")
}
if e.Config.Charts.Num > 0 && e.Config.Charts.Dims <= 0 {
return errors.New("'charts->dimensions' must be > 0")
}
if e.Config.HiddenCharts.Num > 0 && e.Config.HiddenCharts.Dims <= 0 {
return errors.New("'hidden_charts->dimensions' must be > 0")
}
return nil
}
func (e *Example) initCharts() (*module.Charts, error) {
charts := &module.Charts{}
var ctx int
v := calcContextEvery(e.Config.Charts.Num, e.Config.Charts.Contexts)
for i := 0; i < e.Config.Charts.Num; i++ {
if i != 0 && v != 0 && ctx < (e.Config.Charts.Contexts-1) && i%v == 0 {
ctx++
}
chart := newChart(i, ctx, e.Config.Charts.Labels, module.ChartType(e.Config.Charts.Type))
if err := charts.Add(chart); err != nil {
return nil, err
}
}
ctx = 0
v = calcContextEvery(e.Config.HiddenCharts.Num, e.Config.HiddenCharts.Contexts)
for i := 0; i < e.Config.HiddenCharts.Num; i++ {
if i != 0 && v != 0 && ctx < (e.Config.HiddenCharts.Contexts-1) && i%v == 0 {
ctx++
}
chart := newHiddenChart(i, ctx, e.Config.HiddenCharts.Labels, module.ChartType(e.Config.HiddenCharts.Type))
if err := charts.Add(chart); err != nil {
return nil, err
}
}
return charts, nil
}
func calcContextEvery(charts, contexts int) int {
if contexts <= 1 {
return 0
}
if contexts > charts {
return 1
}
return charts / contexts
}

View file

@ -30,7 +30,6 @@ import (
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/dovecot"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/elasticsearch"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/envoy"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/example"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/exim"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/fail2ban"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/filecheck"
@ -101,6 +100,7 @@ import (
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/supervisord"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/systemdunits"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/tengine"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/testrandom"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/tomcat"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/tor"
_ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/traefik"

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package example
package testrandom
import (
"fmt"
@ -13,7 +13,7 @@ var chartTemplate = module.Chart{
Title: "A Random Number",
Units: "random",
Fam: "random",
Ctx: "example.random",
Ctx: "testrandom.random",
}
var hiddenChartTemplate = module.Chart{
@ -21,7 +21,7 @@ var hiddenChartTemplate = module.Chart{
Title: "A Random Number",
Units: "random",
Fam: "random",
Ctx: "example.random",
Ctx: "testrandom.random",
Opts: module.Opts{
Hidden: true,
},
@ -36,8 +36,8 @@ func newChart(num, ctx, labels int, typ module.ChartType) *module.Chart {
}
for i := 0; i < labels; i++ {
chart.Labels = append(chart.Labels, module.Label{
Key: fmt.Sprintf("example_name_%d", i),
Value: fmt.Sprintf("example_value_%d_%d", num, i),
Key: fmt.Sprintf("random_name_%d", i),
Value: fmt.Sprintf("random_value_%d_%d", num, i),
})
}
return chart
@ -52,8 +52,8 @@ func newHiddenChart(num, ctx, labels int, typ module.ChartType) *module.Chart {
}
for i := 0; i < labels; i++ {
chart.Labels = append(chart.Labels, module.Label{
Key: fmt.Sprintf("example_name_%d", i),
Value: fmt.Sprintf("example_value_%d_%d", num, i),
Key: fmt.Sprintf("random_name_%d", i),
Value: fmt.Sprintf("random_value_%d_%d", num, i),
})
}
return chart

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package example
package testrandom
import (
"fmt"
@ -8,40 +8,40 @@ import (
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
)
func (e *Example) collect() (map[string]int64, error) {
func (tr *TestRandom) collect() (map[string]int64, error) {
collected := make(map[string]int64)
for _, chart := range *e.Charts() {
e.collectChart(collected, chart)
for _, chart := range *tr.Charts() {
tr.collectChart(collected, chart)
}
return collected, nil
}
func (e *Example) collectChart(collected map[string]int64, chart *module.Chart) {
func (tr *TestRandom) collectChart(collected map[string]int64, chart *module.Chart) {
var num int
if chart.Opts.Hidden {
num = e.Config.HiddenCharts.Dims
num = tr.Config.HiddenCharts.Dims
} else {
num = e.Config.Charts.Dims
num = tr.Config.Charts.Dims
}
for i := 0; i < num; i++ {
name := fmt.Sprintf("random%d", i)
id := fmt.Sprintf("%s_%s", chart.ID, name)
if !e.collectedDims[id] {
e.collectedDims[id] = true
if !tr.collectedDims[id] {
tr.collectedDims[id] = true
dim := &module.Dim{ID: id, Name: name}
if err := chart.AddDim(dim); err != nil {
e.Warning(err)
tr.Warning(err)
}
chart.MarkNotCreated()
}
if i%2 == 0 {
collected[id] = e.randInt()
collected[id] = tr.randInt()
} else {
collected[id] = -e.randInt()
collected[id] = -tr.randInt()
}
}
}

View file

@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package testrandom
import (
"errors"
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
)
func (tr *TestRandom) validateConfig() error {
if tr.Config.Charts.Num <= 0 && tr.Config.HiddenCharts.Num <= 0 {
return errors.New("'charts->num' or `hidden_charts->num` must be > 0")
}
if tr.Config.Charts.Num > 0 && tr.Config.Charts.Dims <= 0 {
return errors.New("'charts->dimensions' must be > 0")
}
if tr.Config.HiddenCharts.Num > 0 && tr.Config.HiddenCharts.Dims <= 0 {
return errors.New("'hidden_charts->dimensions' must be > 0")
}
return nil
}
func (tr *TestRandom) initCharts() (*module.Charts, error) {
charts := &module.Charts{}
var ctx int
v := calcContextEvery(tr.Config.Charts.Num, tr.Config.Charts.Contexts)
for i := 0; i < tr.Config.Charts.Num; i++ {
if i != 0 && v != 0 && ctx < (tr.Config.Charts.Contexts-1) && i%v == 0 {
ctx++
}
chart := newChart(i, ctx, tr.Config.Charts.Labels, module.ChartType(tr.Config.Charts.Type))
if err := charts.Add(chart); err != nil {
return nil, err
}
}
ctx = 0
v = calcContextEvery(tr.Config.HiddenCharts.Num, tr.Config.HiddenCharts.Contexts)
for i := 0; i < tr.Config.HiddenCharts.Num; i++ {
if i != 0 && v != 0 && ctx < (tr.Config.HiddenCharts.Contexts-1) && i%v == 0 {
ctx++
}
chart := newHiddenChart(i, ctx, tr.Config.HiddenCharts.Labels, module.ChartType(tr.Config.HiddenCharts.Type))
if err := charts.Add(chart); err != nil {
return nil, err
}
}
return charts, nil
}
func calcContextEvery(charts, contexts int) int {
if contexts <= 1 {
return 0
}
if contexts > charts {
return 1
}
return charts / contexts
}

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package example
package testrandom
import (
_ "embed"
@ -13,7 +13,7 @@ import (
var configSchema string
func init() {
module.Register("example", module.Creator{
module.Register("testrandom", module.Creator{
JobConfigSchema: configSchema,
Defaults: module.Defaults{
UpdateEvery: module.UpdateEvery,
@ -25,8 +25,8 @@ func init() {
})
}
func New() *Example {
return &Example{
func New() *TestRandom {
return &TestRandom{
Config: Config{
Charts: ConfigCharts{
Num: 1,
@ -58,7 +58,7 @@ type (
}
)
type Example struct {
type TestRandom struct {
module.Base // should be embedded by every module
Config `yaml:",inline"`
@ -67,38 +67,38 @@ type Example struct {
collectedDims map[string]bool
}
func (e *Example) Configuration() any {
return e.Config
func (tr *TestRandom) Configuration() any {
return tr.Config
}
func (e *Example) Init() error {
err := e.validateConfig()
func (tr *TestRandom) Init() error {
err := tr.validateConfig()
if err != nil {
e.Errorf("config validation: %v", err)
tr.Errorf("config validation: %v", err)
return err
}
charts, err := e.initCharts()
charts, err := tr.initCharts()
if err != nil {
e.Errorf("charts init: %v", err)
tr.Errorf("charts init: %v", err)
return err
}
e.charts = charts
tr.charts = charts
return nil
}
func (e *Example) Check() error {
func (tr *TestRandom) Check() error {
return nil
}
func (e *Example) Charts() *module.Charts {
return e.charts
func (tr *TestRandom) Charts() *module.Charts {
return tr.charts
}
func (e *Example) Collect() map[string]int64 {
mx, err := e.collect()
func (tr *TestRandom) Collect() map[string]int64 {
mx, err := tr.collect()
if err != nil {
e.Error(err)
tr.Error(err)
}
if len(mx) == 0 {
@ -107,4 +107,4 @@ func (e *Example) Collect() map[string]int64 {
return mx
}
func (e *Example) Cleanup() {}
func (tr *TestRandom) Cleanup() {}

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package example
package testrandom
import (
"os"
@ -26,23 +26,15 @@ func Test_testDataIsValid(t *testing.T) {
}
}
func TestExample_ConfigurationSerialize(t *testing.T) {
module.TestConfigurationSerialize(t, &Example{}, dataConfigJSON, dataConfigYAML)
func TestTestRandom_ConfigurationSerialize(t *testing.T) {
module.TestConfigurationSerialize(t, &TestRandom{}, dataConfigJSON, dataConfigYAML)
}
func TestNew(t *testing.T) {
// We want to ensure that module is a reference type, nothing more.
assert.IsType(t, (*Example)(nil), New())
assert.IsType(t, (*TestRandom)(nil), New())
}
func TestExample_Init(t *testing.T) {
// 'Init() bool' initializes the module with an appropriate config, so to test it we need:
// - provide the config.
// - set module.Config field with the config.
// - call Init() and compare its return value with the expected value.
// 'test' map contains different test cases.
func TestTestRandom_Init(t *testing.T) {
tests := map[string]struct {
config Config
wantFail bool
@ -113,106 +105,87 @@ func TestExample_Init(t *testing.T) {
for name, test := range tests {
t.Run(name, func(t *testing.T) {
example := New()
example.Config = test.config
tr := New()
tr.Config = test.config
if test.wantFail {
assert.Error(t, example.Init())
assert.Error(t, tr.Init())
} else {
assert.NoError(t, example.Init())
assert.NoError(t, tr.Init())
}
})
}
}
func TestExample_Check(t *testing.T) {
// 'Check() bool' reports whether the module is able to collect any data, so to test it we need:
// - provide the module with a specific config.
// - initialize the module (call Init()).
// - call Check() and compare its return value with the expected value.
// 'test' map contains different test cases.
func TestTestRandom_Check(t *testing.T) {
tests := map[string]struct {
prepare func() *Example
prepare func() *TestRandom
wantFail bool
}{
"success on default": {prepare: prepareExampleDefault},
"success when only 'charts' set": {prepare: prepareExampleOnlyCharts},
"success when only 'hidden_charts' set": {prepare: prepareExampleOnlyHiddenCharts},
"success when 'charts' and 'hidden_charts' set": {prepare: prepareExampleChartsAndHiddenCharts},
"success on default": {prepare: prepareTRDefault},
"success when only 'charts' set": {prepare: prepareTROnlyCharts},
"success when only 'hidden_charts' set": {prepare: prepareTROnlyHiddenCharts},
"success when 'charts' and 'hidden_charts' set": {prepare: prepareTRChartsAndHiddenCharts},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
example := test.prepare()
require.NoError(t, example.Init())
tr := test.prepare()
require.NoError(t, tr.Init())
if test.wantFail {
assert.Error(t, example.Check())
assert.Error(t, tr.Check())
} else {
assert.NoError(t, example.Check())
assert.NoError(t, tr.Check())
}
})
}
}
func TestExample_Charts(t *testing.T) {
// We want to ensure that initialized module does not return 'nil'.
// If it is not 'nil' we are ok.
// 'test' map contains different test cases.
func TestTestRandom_Charts(t *testing.T) {
tests := map[string]struct {
prepare func(t *testing.T) *Example
prepare func(t *testing.T) *TestRandom
wantNil bool
}{
"not initialized collector": {
wantNil: true,
prepare: func(t *testing.T) *Example {
prepare: func(t *testing.T) *TestRandom {
return New()
},
},
"initialized collector": {
prepare: func(t *testing.T) *Example {
example := New()
require.NoError(t, example.Init())
return example
prepare: func(t *testing.T) *TestRandom {
tr := New()
require.NoError(t, tr.Init())
return tr
},
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
example := test.prepare(t)
tr := test.prepare(t)
if test.wantNil {
assert.Nil(t, example.Charts())
assert.Nil(t, tr.Charts())
} else {
assert.NotNil(t, example.Charts())
assert.NotNil(t, tr.Charts())
}
})
}
}
func TestExample_Cleanup(t *testing.T) {
// Since this module has nothing to clean up,
// we want just to ensure that Cleanup() not panics.
func TestTestRandom_Cleanup(t *testing.T) {
assert.NotPanics(t, New().Cleanup)
}
func TestExample_Collect(t *testing.T) {
// 'Collect() map[string]int64' returns collected data, so to test it we need:
// - provide the module with a specific config.
// - initialize the module (call Init()).
// - call Collect() and compare its return value with the expected value.
// 'test' map contains different test cases.
func TestTestRandom_Collect(t *testing.T) {
tests := map[string]struct {
prepare func() *Example
prepare func() *TestRandom
wantCollected map[string]int64
}{
"default config": {
prepare: prepareExampleDefault,
prepare: prepareTRDefault,
wantCollected: map[string]int64{
"random_0_random0": 1,
"random_0_random1": -1,
@ -221,7 +194,7 @@ func TestExample_Collect(t *testing.T) {
},
},
"only 'charts' set": {
prepare: prepareExampleOnlyCharts,
prepare: prepareTROnlyCharts,
wantCollected: map[string]int64{
"random_0_random0": 1,
"random_0_random1": -1,
@ -236,7 +209,7 @@ func TestExample_Collect(t *testing.T) {
},
},
"only 'hidden_charts' set": {
prepare: prepareExampleOnlyHiddenCharts,
prepare: prepareTROnlyHiddenCharts,
wantCollected: map[string]int64{
"hidden_random_0_random0": 1,
"hidden_random_0_random1": -1,
@ -251,7 +224,7 @@ func TestExample_Collect(t *testing.T) {
},
},
"'charts' and 'hidden_charts' set": {
prepare: prepareExampleChartsAndHiddenCharts,
prepare: prepareTRChartsAndHiddenCharts,
wantCollected: map[string]int64{
"hidden_random_0_random0": 1,
"hidden_random_0_random1": -1,
@ -279,41 +252,23 @@ func TestExample_Collect(t *testing.T) {
for name, test := range tests {
t.Run(name, func(t *testing.T) {
example := test.prepare()
require.NoError(t, example.Init())
tr := test.prepare()
require.NoError(t, tr.Init())
collected := example.Collect()
mx := tr.Collect()
assert.Equal(t, test.wantCollected, collected)
ensureCollectedHasAllChartsDimsVarsIDs(t, example, collected)
assert.Equal(t, test.wantCollected, mx)
module.TestMetricsHasAllChartsDims(t, tr.Charts(), mx)
})
}
}
func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, e *Example, collected map[string]int64) {
for _, chart := range *e.Charts() {
if chart.Obsolete {
continue
}
for _, dim := range chart.Dims {
_, ok := collected[dim.ID]
assert.Truef(t, ok,
"collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID)
}
for _, v := range chart.Vars {
_, ok := collected[v.ID]
assert.Truef(t, ok,
"collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID)
}
}
func prepareTRDefault() *TestRandom {
return prepareTR(New().Config)
}
func prepareExampleDefault() *Example {
return prepareExample(New().Config)
}
func prepareExampleOnlyCharts() *Example {
return prepareExample(Config{
func prepareTROnlyCharts() *TestRandom {
return prepareTR(Config{
Charts: ConfigCharts{
Num: 2,
Dims: 5,
@ -321,8 +276,8 @@ func prepareExampleOnlyCharts() *Example {
})
}
func prepareExampleOnlyHiddenCharts() *Example {
return prepareExample(Config{
func prepareTROnlyHiddenCharts() *TestRandom {
return prepareTR(Config{
HiddenCharts: ConfigCharts{
Num: 2,
Dims: 5,
@ -330,8 +285,8 @@ func prepareExampleOnlyHiddenCharts() *Example {
})
}
func prepareExampleChartsAndHiddenCharts() *Example {
return prepareExample(Config{
func prepareTRChartsAndHiddenCharts() *TestRandom {
return prepareTR(Config{
Charts: ConfigCharts{
Num: 2,
Dims: 5,
@ -343,9 +298,9 @@ func prepareExampleChartsAndHiddenCharts() *Example {
})
}
func prepareExample(cfg Config) *Example {
example := New()
example.Config = cfg
example.randInt = func() int64 { return 1 }
return example
func prepareTR(cfg Config) *TestRandom {
tr := New()
tr.Config = cfg
tr.randInt = func() int64 { return 1 }
return tr
}