261 lines
6.6 KiB
Go
261 lines
6.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 autodiscover
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/elastic/beats/libbeat/autodiscover/meta"
|
|
"github.com/elastic/beats/libbeat/beat"
|
|
"github.com/elastic/beats/libbeat/cfgfile"
|
|
"github.com/elastic/beats/libbeat/common"
|
|
"github.com/elastic/beats/libbeat/common/bus"
|
|
"github.com/elastic/beats/libbeat/common/reload"
|
|
"github.com/elastic/beats/libbeat/logp"
|
|
)
|
|
|
|
const (
|
|
debugK = "autodiscover"
|
|
|
|
// If a config reload fails after a new event, a new reload will be run after this period
|
|
retryPeriod = 10 * time.Second
|
|
)
|
|
|
|
// TODO autodiscover providers config reload
|
|
|
|
// Adapter must be implemented by the beat in order to provide Autodiscover
|
|
type Adapter interface {
|
|
|
|
// CreateConfig generates a valid list of configs from the given event, the received event will have all keys defined by `StartFilter`
|
|
CreateConfig(bus.Event) ([]*common.Config, error)
|
|
|
|
// RunnerFactory provides runner creation by feeding valid configs
|
|
cfgfile.RunnerFactory
|
|
|
|
// EventFilter returns the bus filter to retrieve runner start/stop triggering events
|
|
EventFilter() []string
|
|
}
|
|
|
|
// Autodiscover process, it takes a beat adapter and user config and runs autodiscover process, spawning
|
|
// new modules when any configured providers does a match
|
|
type Autodiscover struct {
|
|
bus bus.Bus
|
|
defaultPipeline beat.Pipeline
|
|
adapter Adapter
|
|
providers []Provider
|
|
configs map[uint64]*reload.ConfigWithMeta
|
|
runners *cfgfile.RunnerList
|
|
meta *meta.Map
|
|
|
|
listener bus.Listener
|
|
}
|
|
|
|
// NewAutodiscover instantiates and returns a new Autodiscover manager
|
|
func NewAutodiscover(name string, pipeline beat.Pipeline, adapter Adapter, config *Config) (*Autodiscover, error) {
|
|
// Init Event bus
|
|
bus := bus.New(name)
|
|
|
|
// Init providers
|
|
var providers []Provider
|
|
for _, providerCfg := range config.Providers {
|
|
provider, err := Registry.BuildProvider(bus, providerCfg)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error in autodiscover provider settings")
|
|
}
|
|
logp.Debug(debugK, "Configured autodiscover provider: %s", provider)
|
|
providers = append(providers, provider)
|
|
}
|
|
|
|
return &Autodiscover{
|
|
bus: bus,
|
|
defaultPipeline: pipeline,
|
|
adapter: adapter,
|
|
configs: map[uint64]*reload.ConfigWithMeta{},
|
|
runners: cfgfile.NewRunnerList("autodiscover", adapter, pipeline),
|
|
providers: providers,
|
|
meta: meta.NewMap(),
|
|
}, nil
|
|
}
|
|
|
|
// Start autodiscover process
|
|
func (a *Autodiscover) Start() {
|
|
if a == nil {
|
|
return
|
|
}
|
|
|
|
logp.Info("Starting autodiscover manager")
|
|
a.listener = a.bus.Subscribe(a.adapter.EventFilter()...)
|
|
|
|
for _, provider := range a.providers {
|
|
provider.Start()
|
|
}
|
|
|
|
go a.worker()
|
|
}
|
|
|
|
func (a *Autodiscover) worker() {
|
|
var updated, retry bool
|
|
|
|
for {
|
|
select {
|
|
case event := <-a.listener.Events():
|
|
// This will happen on Stop:
|
|
if event == nil {
|
|
return
|
|
}
|
|
|
|
if _, ok := event["start"]; ok {
|
|
updated = a.handleStart(event)
|
|
}
|
|
if _, ok := event["stop"]; ok {
|
|
updated = a.handleStop(event)
|
|
}
|
|
|
|
case <-time.After(retryPeriod):
|
|
}
|
|
|
|
if updated || retry {
|
|
if retry {
|
|
logp.Debug(debugK, "Reloading existing autodiscover configs after error")
|
|
}
|
|
|
|
configs := make([]*reload.ConfigWithMeta, 0, len(a.configs))
|
|
for _, c := range a.configs {
|
|
configs = append(configs, c)
|
|
}
|
|
|
|
err := a.runners.Reload(configs)
|
|
|
|
// On error, make sure the next run also updates because some runners were not properly loaded
|
|
retry = err != nil
|
|
// reset updated status
|
|
updated = false
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *Autodiscover) handleStart(event bus.Event) bool {
|
|
var updated bool
|
|
|
|
configs, err := a.adapter.CreateConfig(event)
|
|
if err != nil {
|
|
logp.Debug(debugK, "Could not generate config from event %v: %v", event, err)
|
|
return false
|
|
}
|
|
logp.Debug(debugK, "Got a start event: %v, generated configs: %+v", event, configs)
|
|
|
|
meta := getMeta(event)
|
|
for _, config := range configs {
|
|
hash, err := cfgfile.HashConfig(config)
|
|
if err != nil {
|
|
logp.Debug(debugK, "Could not hash config %v: %v", config, err)
|
|
continue
|
|
}
|
|
|
|
err = a.adapter.CheckConfig(config)
|
|
if err != nil {
|
|
logp.Debug(debugK, "Check failed for config %v: %v, won't start runner", config, err)
|
|
continue
|
|
}
|
|
|
|
// Update meta no matter what
|
|
dynFields := a.meta.Store(hash, meta)
|
|
|
|
if a.runners.Has(hash) {
|
|
logp.Debug(debugK, "Config %v is already running", config)
|
|
continue
|
|
}
|
|
|
|
a.configs[hash] = &reload.ConfigWithMeta{
|
|
Config: config,
|
|
Meta: &dynFields,
|
|
}
|
|
updated = true
|
|
}
|
|
|
|
return updated
|
|
}
|
|
|
|
func (a *Autodiscover) handleStop(event bus.Event) bool {
|
|
var updated bool
|
|
|
|
configs, err := a.adapter.CreateConfig(event)
|
|
if err != nil {
|
|
logp.Debug(debugK, "Could not generate config from event %v: %v", event, err)
|
|
return false
|
|
}
|
|
logp.Debug(debugK, "Got a stop event: %v, generated configs: %+v", event, configs)
|
|
|
|
for _, config := range configs {
|
|
hash, err := cfgfile.HashConfig(config)
|
|
if err != nil {
|
|
logp.Debug(debugK, "Could not hash config %v: %v", config, err)
|
|
continue
|
|
}
|
|
|
|
if !a.runners.Has(hash) {
|
|
logp.Debug(debugK, "Config %v is not running", config)
|
|
continue
|
|
}
|
|
|
|
if a.runners.Has(hash) {
|
|
delete(a.configs, hash)
|
|
updated = true
|
|
} else {
|
|
logp.Debug(debugK, "Runner not found for stopping: %s", hash)
|
|
}
|
|
}
|
|
|
|
return updated
|
|
}
|
|
|
|
func getMeta(event bus.Event) common.MapStr {
|
|
m := event["meta"]
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
|
|
logp.Debug(debugK, "Got a meta field in the event")
|
|
meta, ok := m.(common.MapStr)
|
|
if !ok {
|
|
logp.Err("Got a wrong meta field for event %v", event)
|
|
return nil
|
|
}
|
|
return meta
|
|
}
|
|
|
|
// Stop autodiscover process
|
|
func (a *Autodiscover) Stop() {
|
|
if a == nil {
|
|
return
|
|
}
|
|
|
|
// Stop listening for events
|
|
a.listener.Stop()
|
|
|
|
// Stop providers
|
|
for _, provider := range a.providers {
|
|
provider.Stop()
|
|
}
|
|
|
|
// Stop runners
|
|
a.runners.Stop()
|
|
logp.Info("Stopped autodiscover manager")
|
|
}
|