299 lines
8.1 KiB
Go
299 lines
8.1 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 beater
|
||
|
|
||
|
import (
|
||
|
"sync"
|
||
|
|
||
|
"github.com/elastic/beats/libbeat/common/reload"
|
||
|
"github.com/elastic/beats/libbeat/management"
|
||
|
|
||
|
"github.com/joeshaw/multierror"
|
||
|
"github.com/pkg/errors"
|
||
|
|
||
|
"github.com/elastic/beats/libbeat/autodiscover"
|
||
|
"github.com/elastic/beats/libbeat/beat"
|
||
|
"github.com/elastic/beats/libbeat/cfgfile"
|
||
|
"github.com/elastic/beats/libbeat/common"
|
||
|
"github.com/elastic/beats/libbeat/common/cfgwarn"
|
||
|
"github.com/elastic/beats/libbeat/logp"
|
||
|
"github.com/elastic/beats/metricbeat/mb"
|
||
|
"github.com/elastic/beats/metricbeat/mb/module"
|
||
|
|
||
|
// Add autodiscover builders / appenders
|
||
|
_ "github.com/elastic/beats/metricbeat/autodiscover"
|
||
|
|
||
|
// Add metricbeat default processors
|
||
|
_ "github.com/elastic/beats/metricbeat/processor/add_kubernetes_metadata"
|
||
|
)
|
||
|
|
||
|
// Metricbeat implements the Beater interface for metricbeat.
|
||
|
type Metricbeat struct {
|
||
|
done chan struct{} // Channel used to initiate shutdown.
|
||
|
modules []staticModule // Active list of modules.
|
||
|
config Config
|
||
|
autodiscover *autodiscover.Autodiscover
|
||
|
|
||
|
// Options
|
||
|
moduleOptions []module.Option
|
||
|
}
|
||
|
|
||
|
type staticModule struct {
|
||
|
connector *module.Connector
|
||
|
module *module.Wrapper
|
||
|
}
|
||
|
|
||
|
// Option specifies some optional arguments used for configuring the behavior
|
||
|
// of the Metricbeat framework.
|
||
|
type Option func(mb *Metricbeat)
|
||
|
|
||
|
// WithModuleOptions sets the given module options on the Metricbeat framework
|
||
|
// and these options will be used anytime a new module is instantiated.
|
||
|
func WithModuleOptions(options ...module.Option) Option {
|
||
|
return func(mb *Metricbeat) {
|
||
|
mb.moduleOptions = append(mb.moduleOptions, options...)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Creator returns a beat.Creator for instantiating a new instance of the
|
||
|
// Metricbeat framework with the given options.
|
||
|
func Creator(options ...Option) beat.Creator {
|
||
|
return func(b *beat.Beat, c *common.Config) (beat.Beater, error) {
|
||
|
return newMetricbeat(b, c, options...)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DefaultCreator returns a beat.Creator for instantiating a new instance of
|
||
|
// Metricbeat framework with the traditional Metricbeat module option of
|
||
|
// module.WithMetricSetInfo.
|
||
|
//
|
||
|
// This is equivalent to calling
|
||
|
//
|
||
|
// beater.Creator(
|
||
|
// beater.WithModuleOptions(
|
||
|
// module.WithMetricSetInfo(),
|
||
|
// ),
|
||
|
// )
|
||
|
func DefaultCreator() beat.Creator {
|
||
|
return Creator(
|
||
|
WithModuleOptions(
|
||
|
module.WithMetricSetInfo(),
|
||
|
),
|
||
|
)
|
||
|
}
|
||
|
|
||
|
// newMetricbeat creates and returns a new Metricbeat instance.
|
||
|
func newMetricbeat(b *beat.Beat, c *common.Config, options ...Option) (*Metricbeat, error) {
|
||
|
// List all registered modules and metricsets.
|
||
|
logp.Debug("modules", "%s", mb.Registry.String())
|
||
|
|
||
|
config := defaultConfig
|
||
|
if err := c.Unpack(&config); err != nil {
|
||
|
return nil, errors.Wrap(err, "error reading configuration file")
|
||
|
}
|
||
|
|
||
|
dynamicCfgEnabled := config.ConfigModules.Enabled() || config.Autodiscover != nil || b.ConfigManager.Enabled()
|
||
|
if !dynamicCfgEnabled && len(config.Modules) == 0 {
|
||
|
return nil, mb.ErrEmptyConfig
|
||
|
}
|
||
|
|
||
|
metricbeat := &Metricbeat{
|
||
|
done: make(chan struct{}),
|
||
|
config: config,
|
||
|
}
|
||
|
for _, applyOption := range options {
|
||
|
applyOption(metricbeat)
|
||
|
}
|
||
|
|
||
|
if b.InSetupCmd {
|
||
|
// Return without instantiating the metricsets.
|
||
|
return metricbeat, nil
|
||
|
}
|
||
|
|
||
|
moduleOptions := append(
|
||
|
[]module.Option{module.WithMaxStartDelay(config.MaxStartDelay)},
|
||
|
metricbeat.moduleOptions...)
|
||
|
var errs multierror.Errors
|
||
|
for _, moduleCfg := range config.Modules {
|
||
|
if !moduleCfg.Enabled() {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
failed := false
|
||
|
|
||
|
err := cfgwarn.CheckRemoved5xSettings(moduleCfg, "filters")
|
||
|
if err != nil {
|
||
|
errs = append(errs, err)
|
||
|
failed = true
|
||
|
}
|
||
|
|
||
|
connector, err := module.NewConnector(b.Publisher, moduleCfg, nil)
|
||
|
if err != nil {
|
||
|
errs = append(errs, err)
|
||
|
failed = true
|
||
|
}
|
||
|
|
||
|
module, err := module.NewWrapper(moduleCfg, mb.Registry, moduleOptions...)
|
||
|
if err != nil {
|
||
|
errs = append(errs, err)
|
||
|
failed = true
|
||
|
}
|
||
|
|
||
|
if failed {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
metricbeat.modules = append(metricbeat.modules, staticModule{
|
||
|
connector: connector,
|
||
|
module: module,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
if err := errs.Err(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if len(metricbeat.modules) == 0 && !dynamicCfgEnabled {
|
||
|
return nil, mb.ErrAllModulesDisabled
|
||
|
}
|
||
|
|
||
|
if config.Autodiscover != nil {
|
||
|
var err error
|
||
|
factory := module.NewFactory(metricbeat.moduleOptions...)
|
||
|
adapter := autodiscover.NewFactoryAdapter(factory)
|
||
|
metricbeat.autodiscover, err = autodiscover.NewAutodiscover("metricbeat", b.Publisher, adapter, config.Autodiscover)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return metricbeat, nil
|
||
|
}
|
||
|
|
||
|
// Run starts the workers for Metricbeat and blocks until Stop is called
|
||
|
// and the workers complete. Each host associated with a MetricSet is given its
|
||
|
// own goroutine for fetching data. The ensures that each host is isolated so
|
||
|
// that a single unresponsive host cannot inadvertently block other hosts
|
||
|
// within the same Module and MetricSet from collection.
|
||
|
func (bt *Metricbeat) Run(b *beat.Beat) error {
|
||
|
var wg sync.WaitGroup
|
||
|
|
||
|
// Static modules (metricbeat.modules)
|
||
|
for _, m := range bt.modules {
|
||
|
client, err := m.connector.Connect()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
r := module.NewRunner(client, m.module)
|
||
|
r.Start()
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
<-bt.done
|
||
|
r.Stop()
|
||
|
}()
|
||
|
}
|
||
|
|
||
|
// Centrally managed modules
|
||
|
factory := module.NewFactory(bt.moduleOptions...)
|
||
|
modules := cfgfile.NewRunnerList(management.DebugK, factory, b.Publisher)
|
||
|
reload.Register.MustRegisterList(b.Info.Beat+".modules", modules)
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
<-bt.done
|
||
|
modules.Stop()
|
||
|
}()
|
||
|
|
||
|
// Dynamic file based modules (metricbeat.config.modules)
|
||
|
if bt.config.ConfigModules.Enabled() {
|
||
|
moduleReloader := cfgfile.NewReloader(b.Publisher, bt.config.ConfigModules)
|
||
|
|
||
|
if err := moduleReloader.Check(factory); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
go moduleReloader.Run(factory)
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
<-bt.done
|
||
|
moduleReloader.Stop()
|
||
|
}()
|
||
|
}
|
||
|
|
||
|
// Autodiscover (metricbeat.autodiscover)
|
||
|
if bt.autodiscover != nil {
|
||
|
bt.autodiscover.Start()
|
||
|
wg.Add(1)
|
||
|
go func() {
|
||
|
defer wg.Done()
|
||
|
<-bt.done
|
||
|
bt.autodiscover.Stop()
|
||
|
}()
|
||
|
}
|
||
|
|
||
|
wg.Wait()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Stop signals to Metricbeat that it should stop. It closes the "done" channel
|
||
|
// and closes the publisher client associated with each Module.
|
||
|
//
|
||
|
// Stop should only be called a single time. Calling it more than once may
|
||
|
// result in undefined behavior.
|
||
|
func (bt *Metricbeat) Stop() {
|
||
|
close(bt.done)
|
||
|
}
|
||
|
|
||
|
// Modules return a list of all configured modules, including anyone present
|
||
|
// under dynamic config settings.
|
||
|
func (bt *Metricbeat) Modules() ([]*module.Wrapper, error) {
|
||
|
var modules []*module.Wrapper
|
||
|
for _, m := range bt.modules {
|
||
|
modules = append(modules, m.module)
|
||
|
}
|
||
|
|
||
|
// Add dynamic modules
|
||
|
if bt.config.ConfigModules.Enabled() {
|
||
|
config := cfgfile.DefaultDynamicConfig
|
||
|
bt.config.ConfigModules.Unpack(&config)
|
||
|
|
||
|
modulesManager, err := cfgfile.NewGlobManager(config.Path, ".yml", ".disabled")
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "initialization error")
|
||
|
}
|
||
|
|
||
|
for _, file := range modulesManager.ListEnabled() {
|
||
|
confs, err := cfgfile.LoadList(file.Path)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "error loading config files")
|
||
|
}
|
||
|
for _, conf := range confs {
|
||
|
m, err := module.NewWrapper(conf, mb.Registry, bt.moduleOptions...)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "module initialization error")
|
||
|
}
|
||
|
modules = append(modules, m)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return modules, nil
|
||
|
}
|