youtubebeat/vendor/github.com/elastic/beats/metricbeat/beater/metricbeat.go

299 lines
8.1 KiB
Go
Raw Normal View History

2018-11-18 11:08:38 +01:00
// 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
}