youtubebeat/vendor/github.com/elastic/beats/metricbeat/mb/testing/modules.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
}