365 lines
9.6 KiB
Go
365 lines
9.6 KiB
Go
// Licensed to Elasticsearch B.V. under one or more contributor
|
|
// license agreements. See the NOTICE file distributed with
|
|
// this work for additional information regarding copyright
|
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
|
// the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
/*
|
|
Package testing provides utility functions for testing Module and MetricSet
|
|
implementations.
|
|
|
|
MetricSet Example
|
|
|
|
This is an example showing how to use this package to test a MetricSet. By
|
|
using these methods you ensure the MetricSet is instantiated in the same way
|
|
that Metricbeat does it and with the same validations.
|
|
|
|
package mymetricset_test
|
|
|
|
import (
|
|
mbtest "github.com/elastic/beats/metricbeat/mb/testing"
|
|
)
|
|
|
|
func TestFetch(t *testing.T) {
|
|
f := mbtest.NewEventFetcher(t, getConfig())
|
|
event, err := f.Fetch()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), event)
|
|
|
|
// Test event attributes...
|
|
}
|
|
|
|
func getConfig() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"module": "mymodule",
|
|
"metricsets": []string{"status"},
|
|
"hosts": []string{mymodule.GetHostFromEnv()},
|
|
}
|
|
}
|
|
*/
|
|
package testing
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/elastic/beats/libbeat/common"
|
|
"github.com/elastic/beats/metricbeat/mb"
|
|
)
|
|
|
|
type TestModule struct {
|
|
ModName string
|
|
ModConfig mb.ModuleConfig
|
|
RawConfig *common.Config
|
|
}
|
|
|
|
func (m *TestModule) Name() string { return m.ModName }
|
|
func (m *TestModule) Config() mb.ModuleConfig { return m.ModConfig }
|
|
func (m *TestModule) UnpackConfig(to interface{}) error { return m.RawConfig.Unpack(to) }
|
|
|
|
func NewTestModule(t testing.TB, config interface{}) *TestModule {
|
|
c, err := common.NewConfigFrom(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
return &TestModule{RawConfig: c}
|
|
}
|
|
|
|
// 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 {
|
|
c, err := common.NewConfigFrom(config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m, metricsets, err := mb.NewModule(c, mb.Registry)
|
|
if err != nil {
|
|
t.Fatal("failed to create new MetricSet", err)
|
|
}
|
|
if m == nil {
|
|
t.Fatal("no module instantiated")
|
|
}
|
|
|
|
if len(metricsets) != 1 {
|
|
t.Fatal("invalid number of metricsets instantiated")
|
|
}
|
|
|
|
metricset := metricsets[0]
|
|
if metricset == nil {
|
|
t.Fatal("metricset is nil")
|
|
}
|
|
return metricset
|
|
}
|
|
|
|
// NewEventFetcher instantiates a new EventFetcher using the given
|
|
// configuration. The ModuleFactory and MetricSetFactory are obtained from the
|
|
// global Registry.
|
|
func NewEventFetcher(t testing.TB, config interface{}) mb.EventFetcher {
|
|
metricSet := newMetricSet(t, config)
|
|
|
|
fetcher, ok := metricSet.(mb.EventFetcher)
|
|
if !ok {
|
|
t.Fatal("MetricSet does not implement EventFetcher")
|
|
}
|
|
|
|
return fetcher
|
|
}
|
|
|
|
// NewEventsFetcher instantiates a new EventsFetcher using the given
|
|
// configuration. The ModuleFactory and MetricSetFactory are obtained from the
|
|
// global Registry.
|
|
func NewEventsFetcher(t testing.TB, config interface{}) mb.EventsFetcher {
|
|
metricSet := newMetricSet(t, config)
|
|
|
|
fetcher, ok := metricSet.(mb.EventsFetcher)
|
|
if !ok {
|
|
t.Fatal("MetricSet does not implement EventsFetcher")
|
|
}
|
|
|
|
return fetcher
|
|
}
|
|
|
|
func NewReportingMetricSet(t testing.TB, config interface{}) mb.ReportingMetricSet {
|
|
metricSet := newMetricSet(t, config)
|
|
|
|
reportingMetricSet, ok := metricSet.(mb.ReportingMetricSet)
|
|
if !ok {
|
|
t.Fatal("MetricSet does not implement ReportingMetricSet")
|
|
}
|
|
|
|
return reportingMetricSet
|
|
}
|
|
|
|
// ReportingFetch runs the given reporting metricset and returns all of the
|
|
// events and errors that occur during that period.
|
|
func ReportingFetch(metricSet mb.ReportingMetricSet) ([]common.MapStr, []error) {
|
|
r := &capturingReporter{}
|
|
metricSet.Fetch(r)
|
|
return r.events, r.errs
|
|
}
|
|
|
|
// 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)
|
|
|
|
reportingMetricSetV2, ok := metricSet.(mb.ReportingMetricSetV2)
|
|
if !ok {
|
|
t.Fatal("MetricSet does not implement ReportingMetricSetV2")
|
|
}
|
|
|
|
return reportingMetricSetV2
|
|
}
|
|
|
|
// CapturingReporterV2 is a reporter used for testing which stores all events and errors
|
|
type CapturingReporterV2 struct {
|
|
events []mb.Event
|
|
errs []error
|
|
}
|
|
|
|
// Event is used to report an event
|
|
func (r *CapturingReporterV2) Event(event mb.Event) bool {
|
|
r.events = append(r.events, event)
|
|
return true
|
|
}
|
|
|
|
// Error is used to report an error
|
|
func (r *CapturingReporterV2) Error(err error) bool {
|
|
r.errs = append(r.errs, err)
|
|
return true
|
|
}
|
|
|
|
// GetEvents returns all reported events
|
|
func (r *CapturingReporterV2) GetEvents() []mb.Event {
|
|
return r.events
|
|
}
|
|
|
|
// GetErrors returns all reported errors
|
|
func (r *CapturingReporterV2) GetErrors() []error {
|
|
return r.errs
|
|
}
|
|
|
|
// ReportingFetchV2 runs the given reporting metricset and returns all of the
|
|
// events and errors that occur during that period.
|
|
func ReportingFetchV2(metricSet mb.ReportingMetricSetV2) ([]mb.Event, []error) {
|
|
r := &CapturingReporterV2{}
|
|
metricSet.Fetch(r)
|
|
return r.events, r.errs
|
|
}
|
|
|
|
// NewPushMetricSet instantiates a new PushMetricSet using the given
|
|
// configuration. The ModuleFactory and MetricSetFactory are obtained from the
|
|
// global Registry.
|
|
func NewPushMetricSet(t testing.TB, config interface{}) mb.PushMetricSet {
|
|
metricSet := newMetricSet(t, config)
|
|
|
|
pushMetricSet, ok := metricSet.(mb.PushMetricSet)
|
|
if !ok {
|
|
t.Fatal("MetricSet does not implement PushMetricSet")
|
|
}
|
|
|
|
return pushMetricSet
|
|
}
|
|
|
|
type capturingReporter struct {
|
|
events []common.MapStr
|
|
errs []error
|
|
done chan struct{}
|
|
}
|
|
|
|
func (r *capturingReporter) Event(event common.MapStr) bool {
|
|
r.events = append(r.events, event)
|
|
return true
|
|
}
|
|
|
|
func (r *capturingReporter) ErrorWith(err error, meta common.MapStr) bool {
|
|
r.events = append(r.events, meta)
|
|
r.errs = append(r.errs, err)
|
|
return true
|
|
}
|
|
|
|
func (r *capturingReporter) Error(err error) bool {
|
|
r.errs = append(r.errs, err)
|
|
return true
|
|
}
|
|
|
|
func (r *capturingReporter) Done() <-chan struct{} {
|
|
return r.done
|
|
}
|
|
|
|
// RunPushMetricSet run the given push metricset for the specific amount of time
|
|
// and returns all of the events and errors that occur during that period.
|
|
func RunPushMetricSet(duration time.Duration, metricSet mb.PushMetricSet) ([]common.MapStr, []error) {
|
|
r := &capturingReporter{done: make(chan struct{})}
|
|
|
|
// Run the metricset.
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
metricSet.Run(r)
|
|
}()
|
|
|
|
// Let it run for some period, then stop it by closing the done channel.
|
|
time.AfterFunc(duration, func() {
|
|
close(r.done)
|
|
})
|
|
|
|
// Wait for the PushMetricSet to completely stop.
|
|
wg.Wait()
|
|
|
|
// Return all events and errors that were collected.
|
|
return r.events, r.errs
|
|
}
|
|
|
|
// NewPushMetricSetV2 instantiates a new PushMetricSetV2 using the given
|
|
// configuration. The ModuleFactory and MetricSetFactory are obtained from the
|
|
// global Registry.
|
|
func NewPushMetricSetV2(t testing.TB, config interface{}) mb.PushMetricSetV2 {
|
|
metricSet := newMetricSet(t, config)
|
|
|
|
pushMetricSet, ok := metricSet.(mb.PushMetricSetV2)
|
|
if !ok {
|
|
t.Fatal("MetricSet does not implement PushMetricSet")
|
|
}
|
|
|
|
return pushMetricSet
|
|
}
|
|
|
|
// capturingPushReporterV2 stores all the events and errors from a metricset's
|
|
// Run method.
|
|
type capturingPushReporterV2 struct {
|
|
doneC chan struct{}
|
|
eventsC chan mb.Event
|
|
}
|
|
|
|
// report writes an event to the output channel and returns true. If the output
|
|
// is closed it returns false.
|
|
func (r *capturingPushReporterV2) report(event mb.Event) bool {
|
|
select {
|
|
case <-r.doneC:
|
|
// Publisher is stopped.
|
|
return false
|
|
case r.eventsC <- event:
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Event stores the passed-in event into the events array
|
|
func (r *capturingPushReporterV2) Event(event mb.Event) bool {
|
|
return r.report(event)
|
|
}
|
|
|
|
// Error stores the given error into the errors array.
|
|
func (r *capturingPushReporterV2) Error(err error) bool {
|
|
return r.report(mb.Event{Error: err})
|
|
}
|
|
|
|
// Done returns the Done channel for this reporter.
|
|
func (r *capturingPushReporterV2) Done() <-chan struct{} {
|
|
return r.doneC
|
|
}
|
|
|
|
// RunPushMetricSetV2 run the given push metricset for the specific amount of
|
|
// time and returns all of the events and errors that occur during that period.
|
|
func RunPushMetricSetV2(timeout time.Duration, waitEvents int, metricSet mb.PushMetricSetV2) []mb.Event {
|
|
var (
|
|
r = &capturingPushReporterV2{doneC: make(chan struct{}), eventsC: make(chan mb.Event)}
|
|
wg sync.WaitGroup
|
|
events []mb.Event
|
|
)
|
|
wg.Add(2)
|
|
|
|
// Producer
|
|
go func() {
|
|
defer wg.Done()
|
|
defer close(r.eventsC)
|
|
if closer, ok := metricSet.(mb.Closer); ok {
|
|
defer closer.Close()
|
|
}
|
|
metricSet.Run(r)
|
|
}()
|
|
|
|
// Consumer
|
|
go func() {
|
|
defer wg.Done()
|
|
defer close(r.doneC)
|
|
|
|
timer := time.NewTimer(timeout)
|
|
defer timer.Stop()
|
|
for {
|
|
select {
|
|
case <-timer.C:
|
|
return
|
|
case e, ok := <-r.eventsC:
|
|
if !ok {
|
|
return
|
|
}
|
|
events = append(events, e)
|
|
if waitEvents > 0 && waitEvents <= len(events) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
return events
|
|
}
|