Skip to content

Commit

Permalink
[Metricbeat] Check that fields are documented in data tests (#11127)
Browse files Browse the repository at this point in the history
Currently we check in python tests that the fields are documented. As by now we have all the fields also available in the go code with the fields.go files it is possible to do this check in the new data tests.

To prevent cyclic imports the data_test.go had to be moved to its own package.

Now by default all modules are imported. Like this we don't have to add each module manually.
  • Loading branch information
ruflin authored Mar 18, 2019
1 parent feaef40 commit 4dce54b
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 35 deletions.
2 changes: 1 addition & 1 deletion metricbeat/docs/fields.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -12276,7 +12276,7 @@ Request latency, number of requests
--
*`kubernetes.apiserver.request.latency.bucket`*::
*`kubernetes.apiserver.request.latency.bucket.*`*::
+
--
type: object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.

package testing
package data

import (
"encoding/json"
Expand All @@ -31,23 +31,17 @@ import (
"testing"

"github.com/mitchellh/hashstructure"

"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"

"github.com/elastic/beats/libbeat/asset"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/mapping"
"github.com/elastic/beats/metricbeat/mb"
mbtesting "github.com/elastic/beats/metricbeat/mb/testing"

// TODO: generate include file for these tests automatically moving forward
_ "github.com/elastic/beats/metricbeat/module/couchbase/cluster"
_ "github.com/elastic/beats/metricbeat/module/couchbase/node"
_ "github.com/elastic/beats/metricbeat/module/kibana/status"
_ "github.com/elastic/beats/metricbeat/module/kubernetes/apiserver"
_ "github.com/elastic/beats/metricbeat/module/kubernetes/state_node"
_ "github.com/elastic/beats/metricbeat/module/php_fpm/pool"
_ "github.com/elastic/beats/metricbeat/module/php_fpm/process"
_ "github.com/elastic/beats/metricbeat/module/rabbitmq/connection"
_ "github.com/elastic/beats/metricbeat/module/traefik/health"
_ "github.com/elastic/beats/metricbeat/include"
_ "github.com/elastic/beats/metricbeat/include/fields"
)

const (
Expand All @@ -72,8 +66,8 @@ func TestAll(t *testing.T) {
for _, f := range configFiles {
// get module and metricset name from path
s := strings.Split(f, string(os.PathSeparator))
moduleName := s[3]
metricSetName := s[4]
moduleName := s[4]
metricSetName := s[5]

configFile, err := ioutil.ReadFile(f)
if err != nil {
Expand All @@ -95,7 +89,11 @@ func TestAll(t *testing.T) {

func getTestdataFiles(t *testing.T, url, module, metricSet, suffix string) {

ff, _ := filepath.Glob(getMetricsetPath(module, metricSet) + "/_meta/testdata/*." + suffix)
ff, err := filepath.Glob(getMetricsetPath(module, metricSet) + "/_meta/testdata/*." + suffix)
if err != nil {
t.Fatal(err)
}

var files []string
for _, f := range ff {
// Exclude all the expected files
Expand All @@ -118,18 +116,18 @@ func runTest(t *testing.T, file string, module, metricSetName, url, suffix strin
s := server(t, file, url)
defer s.Close()

metricSet := newMetricSet(t, getConfig(module, metricSetName, s.URL))
metricSet := mbtesting.NewMetricSet(t, getConfig(module, metricSetName, s.URL))

var events []mb.Event
var errs []error

switch v := metricSet.(type) {
case mb.ReportingMetricSetV2:
metricSet := NewReportingMetricSetV2(t, getConfig(module, metricSetName, s.URL))
events, errs = ReportingFetchV2(metricSet)
metricSet := mbtesting.NewReportingMetricSetV2(t, getConfig(module, metricSetName, s.URL))
events, errs = mbtesting.ReportingFetchV2(metricSet)
case mb.ReportingMetricSetV2Error:
metricSet := NewReportingMetricSetV2Error(t, getConfig(module, metricSetName, s.URL))
events, errs = ReportingFetchV2Error(metricSet)
metricSet := mbtesting.NewReportingMetricSetV2Error(t, getConfig(module, metricSetName, s.URL))
events, errs = mbtesting.ReportingFetchV2Error(metricSet)
default:
t.Fatalf("unknown type: %T", v)
}
Expand All @@ -143,7 +141,7 @@ func runTest(t *testing.T, file string, module, metricSetName, url, suffix strin
var data []common.MapStr

for _, e := range events {
beatEvent := StandardizeEvent(metricSet, e, mb.AddMetricSetInfo)
beatEvent := mbtesting.StandardizeEvent(metricSet, e, mb.AddMetricSetInfo)
// Overwrite service.address as the port changes every time
beatEvent.Fields.Put("service.address", "127.0.0.1:55555")
data = append(data, beatEvent.Fields)
Expand All @@ -156,6 +154,8 @@ func runTest(t *testing.T, file string, module, metricSetName, url, suffix strin
return h1 < h2
})

checkDocumented(t, data)

output, err := json.MarshalIndent(&data, "", " ")
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -190,6 +190,41 @@ func writeDataJSON(t *testing.T, data common.MapStr, module, metricSet string) {
}
}

// checkDocumented checks that all fields which show up in the events are documented
func checkDocumented(t *testing.T, data []common.MapStr) {
fieldsData, err := asset.GetFields("metricbeat")
if err != nil {
t.Fatal(err)
}

fields, err := mapping.LoadFields(fieldsData)
if err != nil {
t.Fatal(err)
}
documentedFields := fields.GetKeys()
keys := map[string]interface{}{}

for _, k := range documentedFields {
keys[k] = struct{}{}
}

for _, d := range data {
flat := d.Flatten()
for k := range flat {
if _, ok := keys[k]; !ok {
// If a field is defined as object it can also be defined as `status_codes.*`
// So this checks if such a key with the * exists by removing the last part.
splits := strings.Split(k, ".")
prefix := strings.Join(splits[0:len(splits)-1], ".")
if _, ok := keys[prefix+".*"]; ok {
continue
}
t.Fatalf("key missing: %s", k)
}
}
}
}

// GetConfig returns config for elasticsearch module
func getConfig(module, metricSet, url string) map[string]interface{} {
return map[string]interface{}{
Expand Down Expand Up @@ -226,7 +261,7 @@ func server(t *testing.T, path string, url string) *httptest.Server {
}

func getModulesPath() string {
return "../../module"
return "../../../module"
}

func getModulePath(module string) string {
Expand Down
18 changes: 9 additions & 9 deletions metricbeat/mb/testing/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ func NewTestModule(t testing.TB, config interface{}) *TestModule {
return &TestModule{RawConfig: c}
}

// newMetricSet instantiates a new MetricSet using the given configuration.
// NewMetricSet instantiates a new MetricSet using the given configuration.
// The ModuleFactory and MetricSetFactory are obtained from the global
// Registry.
func newMetricSet(t testing.TB, config interface{}) mb.MetricSet {
func NewMetricSet(t testing.TB, config interface{}) mb.MetricSet {
c, err := common.NewConfigFrom(config)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -112,7 +112,7 @@ func newMetricSet(t testing.TB, config interface{}) mb.MetricSet {
// configuration. The ModuleFactory and MetricSetFactory are obtained from the
// global Registry.
func NewEventFetcher(t testing.TB, config interface{}) mb.EventFetcher {
metricSet := newMetricSet(t, config)
metricSet := NewMetricSet(t, config)

fetcher, ok := metricSet.(mb.EventFetcher)
if !ok {
Expand All @@ -126,7 +126,7 @@ func NewEventFetcher(t testing.TB, config interface{}) mb.EventFetcher {
// configuration. The ModuleFactory and MetricSetFactory are obtained from the
// global Registry.
func NewEventsFetcher(t testing.TB, config interface{}) mb.EventsFetcher {
metricSet := newMetricSet(t, config)
metricSet := NewMetricSet(t, config)

fetcher, ok := metricSet.(mb.EventsFetcher)
if !ok {
Expand All @@ -137,7 +137,7 @@ func NewEventsFetcher(t testing.TB, config interface{}) mb.EventsFetcher {
}

func NewReportingMetricSet(t testing.TB, config interface{}) mb.ReportingMetricSet {
metricSet := newMetricSet(t, config)
metricSet := NewMetricSet(t, config)

reportingMetricSet, ok := metricSet.(mb.ReportingMetricSet)
if !ok {
Expand All @@ -158,7 +158,7 @@ func ReportingFetch(metricSet mb.ReportingMetricSet) ([]common.MapStr, []error)
// NewReportingMetricSetV2 returns a new ReportingMetricSetV2 instance. Then
// you can use ReportingFetchV2 to perform a Fetch operation with the MetricSet.
func NewReportingMetricSetV2(t testing.TB, config interface{}) mb.ReportingMetricSetV2 {
metricSet := newMetricSet(t, config)
metricSet := NewMetricSet(t, config)

reportingMetricSetV2, ok := metricSet.(mb.ReportingMetricSetV2)
if !ok {
Expand All @@ -171,7 +171,7 @@ func NewReportingMetricSetV2(t testing.TB, config interface{}) mb.ReportingMetri
// NewReportingMetricSetV2Error returns a new ReportingMetricSetV2 instance. Then
// you can use ReportingFetchV2 to perform a Fetch operation with the MetricSet.
func NewReportingMetricSetV2Error(t testing.TB, config interface{}) mb.ReportingMetricSetV2Error {
metricSet := newMetricSet(t, config)
metricSet := NewMetricSet(t, config)

reportingMetricSetV2Error, ok := metricSet.(mb.ReportingMetricSetV2Error)
if !ok {
Expand Down Expand Up @@ -232,7 +232,7 @@ func ReportingFetchV2Error(metricSet mb.ReportingMetricSetV2Error) ([]mb.Event,
// configuration. The ModuleFactory and MetricSetFactory are obtained from the
// global Registry.
func NewPushMetricSet(t testing.TB, config interface{}) mb.PushMetricSet {
metricSet := newMetricSet(t, config)
metricSet := NewMetricSet(t, config)

pushMetricSet, ok := metricSet.(mb.PushMetricSet)
if !ok {
Expand Down Expand Up @@ -297,7 +297,7 @@ func RunPushMetricSet(duration time.Duration, metricSet mb.PushMetricSet) ([]com
// configuration. The ModuleFactory and MetricSetFactory are obtained from the
// global Registry.
func NewPushMetricSetV2(t testing.TB, config interface{}) mb.PushMetricSetV2 {
metricSet := newMetricSet(t, config)
metricSet := NewMetricSet(t, config)

pushMetricSet, ok := metricSet.(mb.PushMetricSetV2)
if !ok {
Expand Down
4 changes: 2 additions & 2 deletions metricbeat/module/kubernetes/apiserver/_meta/fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
type: long
description: >
Request latency, number of requests
- name: request.latency.bucket
- name: request.latency.bucket.*
type: object
object_type: long
description: >
Request latency histogram buckets
Request latency histogram buckets
2 changes: 1 addition & 1 deletion metricbeat/module/kubernetes/fields.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4dce54b

Please sign in to comment.