From 674b47caf6d11b4e7143b496e32bde599d8f0c62 Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Tue, 25 Jun 2024 11:19:34 -0400 Subject: [PATCH 1/5] Add APM reload. --- libbeat/common/reload/reload.go | 19 +++++++ x-pack/libbeat/management/managerV2.go | 78 ++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/libbeat/common/reload/reload.go b/libbeat/common/reload/reload.go index 279b7fd26b0..b738ba76f16 100644 --- a/libbeat/common/reload/reload.go +++ b/libbeat/common/reload/reload.go @@ -34,6 +34,9 @@ const InputRegName = "input" // OutputRegName is the registation name for V2 Outputs const OutputRegName = "output" +// APMRegName is the registation name for APM tracing. +const APMRegName = "apm" + // ConfigWithMeta holds a pair of config.C and optional metadata for it type ConfigWithMeta struct { // Config to store @@ -140,6 +143,14 @@ func (r *Registry) MustRegisterInput(list ReloadableList) { } } +// MustRegisterAPM is a V2-specific registration function +// that declares a reloadable APM tracing configuration +func (r *Registry) MustRegisterAPM(list Reloadable) { + if err := r.Register(APMRegName, list); err != nil { + panic(err) + } +} + // GetInputList is a V2-specific function // That returns the reloadable list created for an input func (r *Registry) GetInputList() ReloadableList { @@ -156,6 +167,14 @@ func (r *Registry) GetReloadableOutput() Reloadable { return r.confs[OutputRegName] } +// GetReloadableAPM is a V2-specific function +// That returns the reloader for the registered APM trace +func (r *Registry) GetReloadableAPM() Reloadable { + r.RLock() + defer r.RUnlock() + return r.confs[APMRegName] +} + // GetRegisteredNames returns the list of names registered func (r *Registry) GetRegisteredNames() []string { r.RLock() diff --git a/x-pack/libbeat/management/managerV2.go b/x-pack/libbeat/management/managerV2.go index 6152f4f5306..72f2a7ee9ba 100644 --- a/x-pack/libbeat/management/managerV2.go +++ b/x-pack/libbeat/management/managerV2.go @@ -101,10 +101,14 @@ type BeatV2Manager struct { // set with the last applied input configs lastInputCfgs map[string]*proto.UnitExpectedConfig + // set with the last applied APM config + lastAPMCfg *proto.APMConfig + // used for the debug callback to report as-running config lastBeatOutputCfg *reload.ConfigWithMeta lastBeatInputCfgs []*reload.ConfigWithMeta lastBeatFeaturesCfg *conf.C + lastBeatAPMCfg *reload.ConfigWithMeta // changeDebounce is the debounce time for a configuration change changeDebounce time.Duration @@ -642,6 +646,10 @@ func (cm *BeatV2Manager) reload(units map[unitKey]*client.Unit) { cm.logger.Errorw("setting output state", "error", err) } + // reload APM tracing configuration + // all error handling is handled inside of reloadAPM + cm.reloadAPM(outputUnit) + // compute the input configuration // // in v2 only a single input type will be started per component, so we don't need to @@ -837,6 +845,65 @@ func (cm *BeatV2Manager) reloadInputs(inputUnits []*client.Unit) error { return nil } +// reloadAPM reload APM tracing, it returns a bool and an error. +// The bool, if set, indicates that the output reload requires an restart, +// in that case the error is always `nil`. +// +// In any other case, the bool is always false and the error will be non nil +// if any error has occurred. +func (cm *BeatV2Manager) reloadAPM(unit *client.Unit) { + // Assuming that the output reloadable isn't a list, see createBeater() in cmd/instance/beat.go + apm := cm.registry.GetReloadableAPM() + if apm == nil { + // no APM reloadable, nothing to do + cm.logger.Debug("Unable to reload APM tracing; no APM reloadable registered") + return + } + + var apmConfig *proto.APMConfig + if unit != nil { + expected := unit.Expected() + if expected.APMConfig != nil { + apmConfig = expected.APMConfig + } + } + if apmConfig == nil { + // APM tracing is being stopped + cm.logger.Debug("Stopping APM tracing") + err := apm.Reload(nil) + if err != nil { + cm.logger.Errorf("Error stopping APM tracing: %s", err) + return + } + cm.lastAPMCfg = nil + cm.lastBeatAPMCfg = nil + cm.logger.Debug("Stopped APM tracing") + return + } + + if cm.lastAPMCfg != nil && gproto.Equal(cm.lastAPMCfg, apmConfig) { + // configuration for the APM tracing did not change; do nothing + cm.logger.Debug("Skipped reloading APM tracing; configuration didn't change") + return + } + + uconfig, err := conf.NewConfigFrom(apmConfig) + if err != nil { + cm.logger.Errorf("Failed to create uconfig from APM configuration: %s", err) + return + } + reloadConfig := &reload.ConfigWithMeta{Config: uconfig} + cm.logger.Debug("Reloading APM tracing") + err = apm.Reload(reloadConfig) + if err != nil { + cm.logger.Debugf("Error reloading APM tracing: %s", err) + return + } + cm.lastAPMCfg = apmConfig + cm.lastBeatAPMCfg = reloadConfig + cm.logger.Debugf("Reloaded APM tracing") +} + // this function is registered as a debug hook // it prints the last known configuration generated by the beat func (cm *BeatV2Manager) handleDebugYaml() []byte { @@ -871,6 +938,15 @@ func (cm *BeatV2Manager) handleDebugYaml() []byte { } } + // generate APM + var apmCfg map[string]interface{} + if cm.lastBeatAPMCfg != nil { + if err := cm.lastBeatAPMCfg.Config.Unpack(&apmCfg); err != nil { + cm.logger.Errorf("error unpacking APM tracing config for debug callback: %s", err) + return nil + } + } + // combine all of the above in a somewhat coherent way // This isn't perfect, but generating a config that can actually be fed back into the beat // would require @@ -878,10 +954,12 @@ func (cm *BeatV2Manager) handleDebugYaml() []byte { Inputs []map[string]interface{} Outputs map[string]interface{} Features map[string]interface{} + APM map[string]interface{} }{ Inputs: inputList, Outputs: outputCfg, Features: featuresCfg, + APM: apmCfg, } data, err := yaml.Marshal(beatCfg) From d477c9f3206e4490c74458691db68772731ca19b Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Wed, 26 Jun 2024 10:54:49 -0400 Subject: [PATCH 2/5] Add testing. --- libbeat/tests/integration/mockserver.go | 42 +-- .../tests/integration/managerV2_test.go | 39 ++- x-pack/libbeat/management/managerV2_test.go | 252 ++++++++++-------- 3 files changed, 185 insertions(+), 148 deletions(-) diff --git a/libbeat/tests/integration/mockserver.go b/libbeat/tests/integration/mockserver.go index 763467819fa..c7b74a83d86 100644 --- a/libbeat/tests/integration/mockserver.go +++ b/libbeat/tests/integration/mockserver.go @@ -24,7 +24,6 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/structpb" - "github.com/elastic/beats/v7/libbeat/version" "github.com/elastic/elastic-agent-client/v7/pkg/client" "github.com/elastic/elastic-agent-client/v7/pkg/client/mock" "github.com/elastic/elastic-agent-client/v7/pkg/proto" @@ -51,49 +50,32 @@ type unitKey struct { // sent units, the server will wait for `delay` before sending the // next state. This will block the check-in call from the Beat. func NewMockServer( - units [][]*proto.UnitExpected, - featuresIdxs []uint64, - features []*proto.Features, + expected []*proto.CheckinExpected, observedCallback func(*proto.CheckinObserved, int), delay time.Duration, ) *mock.StubServerV2 { i := 0 - agentInfo := &proto.AgentInfo{ - Id: "elastic-agent-id", - Version: version.GetDefaultVersion(), - Snapshot: true, - } return &mock.StubServerV2{ CheckinV2Impl: func(observed *proto.CheckinObserved) *proto.CheckinExpected { if observedCallback != nil { observedCallback(observed, i) } - matches := doesStateMatch(observed, units[i], featuresIdxs[i]) + matches := doesStateMatch(observed, expected[i]) if !matches { // send same set of units and features - return &proto.CheckinExpected{ - AgentInfo: agentInfo, - Units: units[i], - Features: features[i], - FeaturesIdx: featuresIdxs[i], - } + return expected[i] } // delay sending next expected based on delay if delay > 0 { <-time.After(delay) } - // send next set of units and features + // send next expected i += 1 - if i >= len(units) { + if i >= len(expected) { // stay on last index - i = len(units) - 1 - } - return &proto.CheckinExpected{ - AgentInfo: agentInfo, - Units: units[i], - Features: features[i], - FeaturesIdx: featuresIdxs[i], + i = len(expected) - 1 } + return expected[i] }, ActionImpl: func(response *proto.ActionResponse) error { // actions not tested here @@ -105,14 +87,13 @@ func NewMockServer( func doesStateMatch( observed *proto.CheckinObserved, - expectedUnits []*proto.UnitExpected, - expectedFeaturesIdx uint64, + expected *proto.CheckinExpected, ) bool { - if len(observed.Units) != len(expectedUnits) { + if len(observed.Units) != len(expected.Units) { return false } expectedMap := make(map[unitKey]*proto.UnitExpected) - for _, exp := range expectedUnits { + for _, exp := range expected.Units { expectedMap[unitKey{client.UnitType(exp.Type), exp.Id}] = exp } for _, unit := range observed.Units { @@ -124,8 +105,7 @@ func doesStateMatch( return false } } - - return observed.FeaturesIdx == expectedFeaturesIdx + return observed.FeaturesIdx == expected.FeaturesIdx } func RequireNewStruct(t *testing.T, v map[string]interface{}) *structpb.Struct { diff --git a/x-pack/filebeat/tests/integration/managerV2_test.go b/x-pack/filebeat/tests/integration/managerV2_test.go index 1a35aae7d0a..056b0a219a0 100644 --- a/x-pack/filebeat/tests/integration/managerV2_test.go +++ b/x-pack/filebeat/tests/integration/managerV2_test.go @@ -468,20 +468,37 @@ func TestRecoverFromInvalidOutputConfiguration(t *testing.T) { // Those are the 'states' Filebeat will go through. // After each state is reached the mockServer will // send the next. - protoUnits := [][]*proto.UnitExpected{ + agentInfo := &proto.AgentInfo{ + Id: "elastic-agent-id", + Version: version.GetDefaultVersion(), + Snapshot: true, + } + protos := []*proto.CheckinExpected{ { - &healthyOutput, - &filestreamInputHealthy, + AgentInfo: agentInfo, + Units: []*proto.UnitExpected{ + &healthyOutput, + &filestreamInputHealthy, + }, }, { - &brokenOutput, - &filestreamInputStarting, + AgentInfo: agentInfo, + Units: []*proto.UnitExpected{ + &brokenOutput, + &filestreamInputStarting, + }, + }, + { + AgentInfo: agentInfo, + Units: []*proto.UnitExpected{ + &healthyOutput, + &filestreamInputHealthy, + }, }, { - &healthyOutput, - &filestreamInputHealthy, + AgentInfo: agentInfo, + Units: []*proto.UnitExpected{}, // An empty one makes the Beat exit }, - {}, // An empty one makes the Beat exit } // We use `success` to signal the test has ended successfully @@ -489,15 +506,13 @@ func TestRecoverFromInvalidOutputConfiguration(t *testing.T) { success := make(chan struct{}) // The test is successful when we reach the last element of `protoUnits` onObserved := func(observed *proto.CheckinObserved, protoUnitsIdx int) { - if protoUnitsIdx == len(protoUnits)-1 { + if protoUnitsIdx == len(protos)-1 { close(success) } } server := integration.NewMockServer( - protoUnits, - []uint64{0, 0, 0, 0}, - []*proto.Features{nil, nil, nil, nil}, + protos, onObserved, 100*time.Millisecond, ) diff --git a/x-pack/libbeat/management/managerV2_test.go b/x-pack/libbeat/management/managerV2_test.go index 66ca7f17966..90063f82eeb 100644 --- a/x-pack/libbeat/management/managerV2_test.go +++ b/x-pack/libbeat/management/managerV2_test.go @@ -7,6 +7,7 @@ package management import ( "errors" "fmt" + "github.com/elastic/beats/v7/libbeat/version" "sync" "sync/atomic" "testing" @@ -36,6 +37,8 @@ func TestManagerV2(t *testing.T) { r.MustRegisterOutput(output) inputs := &reloadableList{} r.MustRegisterInput(inputs) + apm := &reloadable{} + r.MustRegisterAPM(apm) configsSet := atomic.Bool{} configsCleared := atomic.Bool{} @@ -46,22 +49,25 @@ func TestManagerV2(t *testing.T) { if currentIdx == 1 { oCfg := output.Config() iCfgs := inputs.Configs() - if oCfg != nil && len(iCfgs) == 3 { + apmCfg := apm.Config() + if oCfg != nil && len(iCfgs) == 3 && apmCfg != nil { configsSet.Store(true) - t.Log("output and inputs configuration set") + t.Log("output, inputs, and APM configuration set") } } else if currentIdx == 2 { oCfg := output.Config() iCfgs := inputs.Configs() - if oCfg == nil || len(iCfgs) != 3 { + apmCfg := apm.Config() + if oCfg == nil || len(iCfgs) != 3 || apmCfg == nil { // should not happen (config no longer set) configsSet.Store(false) - t.Log("output and inputs configuration cleared (should not happen)") + t.Log("output, inputs, and APM configuration cleared (should not happen)") } } else { oCfg := output.Config() iCfgs := inputs.Configs() - if oCfg == nil && len(iCfgs) == 0 { + apmCfg := apm.Config() + if oCfg == nil && len(iCfgs) == 0 && apmCfg == nil { configsCleared.Store(true) } if len(observed.Units) == 0 { @@ -78,125 +84,161 @@ func TestManagerV2(t *testing.T) { t.Logf("FQDN feature flag set to %v", fqdnEnabled.Load()) } - srv := integration.NewMockServer([][]*proto.UnitExpected{ + agentInfo := &proto.AgentInfo{ + Id: "elastic-agent-id", + Version: version.GetDefaultVersion(), + Snapshot: true, + } + apmConfig := &proto.ElasticAPM{ + Tls: &proto.ElasticAPMTLS{ + ServerCa: "serverca", + ServerCert: "servercert", + SkipVerify: true, // set to true as false is default when empty + }, + Environment: "unit-testing", + ApiKey: "apikey123", + SecretToken: "secrettoken123", + Hosts: []string{"localhost:8200", "otherhost:8200"}, + GlobalLabels: "label1,label2", + } + srv := integration.NewMockServer([]*proto.CheckinExpected{ { - { - Id: "output-unit", - Type: proto.UnitType_OUTPUT, - ConfigStateIdx: 1, - Config: &proto.UnitExpectedConfig{ - Id: "default", - Type: "elasticsearch", - Name: "elasticsearch", + AgentInfo: agentInfo, + Units: []*proto.UnitExpected{ + { + Id: "output-unit", + Type: proto.UnitType_OUTPUT, + ConfigStateIdx: 1, + Config: &proto.UnitExpectedConfig{ + Id: "default", + Type: "elasticsearch", + Name: "elasticsearch", + }, + State: proto.State_HEALTHY, + LogLevel: proto.UnitLogLevel_INFO, }, - State: proto.State_HEALTHY, - LogLevel: proto.UnitLogLevel_INFO, - }, - { - Id: "input-unit-1", - Type: proto.UnitType_INPUT, - ConfigStateIdx: 1, - Config: &proto.UnitExpectedConfig{ - Id: "system/metrics-system-default-system-1", - Type: "system/metrics", - Name: "system-1", - Streams: []*proto.Stream{ - { - Id: "system/metrics-system.filesystem-default-system-1", - Source: integration.RequireNewStruct(t, map[string]interface{}{ - "metricsets": []interface{}{"filesystem"}, - "period": "1m", - }), + { + Id: "input-unit-1", + Type: proto.UnitType_INPUT, + ConfigStateIdx: 1, + Config: &proto.UnitExpectedConfig{ + Id: "system/metrics-system-default-system-1", + Type: "system/metrics", + Name: "system-1", + Streams: []*proto.Stream{ + { + Id: "system/metrics-system.filesystem-default-system-1", + Source: integration.RequireNewStruct(t, map[string]interface{}{ + "metricsets": []interface{}{"filesystem"}, + "period": "1m", + }), + }, }, }, + State: proto.State_HEALTHY, + LogLevel: proto.UnitLogLevel_INFO, }, - State: proto.State_HEALTHY, - LogLevel: proto.UnitLogLevel_INFO, - }, - { - Id: "input-unit-2", - Type: proto.UnitType_INPUT, - ConfigStateIdx: 1, - Config: &proto.UnitExpectedConfig{ - Id: "system/metrics-system-default-system-2", - Type: "system/metrics", - Name: "system-2", - Streams: []*proto.Stream{ - { - Id: "system/metrics-system.filesystem-default-system-2", - Source: integration.RequireNewStruct(t, map[string]interface{}{ - "metricsets": []interface{}{"filesystem"}, - "period": "1m", - }), - }, - { - Id: "system/metrics-system.filesystem-default-system-3", - Source: integration.RequireNewStruct(t, map[string]interface{}{ - "metricsets": []interface{}{"filesystem"}, - "period": "1m", - }), + { + Id: "input-unit-2", + Type: proto.UnitType_INPUT, + ConfigStateIdx: 1, + Config: &proto.UnitExpectedConfig{ + Id: "system/metrics-system-default-system-2", + Type: "system/metrics", + Name: "system-2", + Streams: []*proto.Stream{ + { + Id: "system/metrics-system.filesystem-default-system-2", + Source: integration.RequireNewStruct(t, map[string]interface{}{ + "metricsets": []interface{}{"filesystem"}, + "period": "1m", + }), + }, + { + Id: "system/metrics-system.filesystem-default-system-3", + Source: integration.RequireNewStruct(t, map[string]interface{}{ + "metricsets": []interface{}{"filesystem"}, + "period": "1m", + }), + }, }, }, + State: proto.State_HEALTHY, + LogLevel: proto.UnitLogLevel_INFO, }, - State: proto.State_HEALTHY, - LogLevel: proto.UnitLogLevel_INFO, }, + Features: nil, + FeaturesIdx: 1, }, { - { - Id: "output-unit", - Type: proto.UnitType_OUTPUT, - ConfigStateIdx: 1, - State: proto.State_HEALTHY, - LogLevel: proto.UnitLogLevel_INFO, - }, - { - Id: "input-unit-1", - Type: proto.UnitType_INPUT, - ConfigStateIdx: 1, - State: proto.State_HEALTHY, - LogLevel: proto.UnitLogLevel_DEBUG, + AgentInfo: agentInfo, + Units: []*proto.UnitExpected{ + { + Id: "output-unit", + Type: proto.UnitType_OUTPUT, + ConfigStateIdx: 1, + State: proto.State_HEALTHY, + LogLevel: proto.UnitLogLevel_INFO, + }, + { + Id: "input-unit-1", + Type: proto.UnitType_INPUT, + ConfigStateIdx: 1, + State: proto.State_HEALTHY, + LogLevel: proto.UnitLogLevel_DEBUG, + }, + { + Id: "input-unit-2", + Type: proto.UnitType_INPUT, + ConfigStateIdx: 1, + State: proto.State_HEALTHY, + LogLevel: proto.UnitLogLevel_INFO, + }, }, - { - Id: "input-unit-2", - Type: proto.UnitType_INPUT, - ConfigStateIdx: 1, - State: proto.State_HEALTHY, - LogLevel: proto.UnitLogLevel_INFO, + Features: &proto.Features{Fqdn: &proto.FQDNFeature{Enabled: true}}, + FeaturesIdx: 2, + Component: &proto.Component{ + ApmConfig: &proto.APMConfig{Elastic: apmConfig}, }, }, { - { - Id: "output-unit", - Type: proto.UnitType_OUTPUT, - ConfigStateIdx: 1, - State: proto.State_STOPPED, - LogLevel: proto.UnitLogLevel_INFO, - }, - { - Id: "input-unit-1", - Type: proto.UnitType_INPUT, - ConfigStateIdx: 1, - State: proto.State_STOPPED, - LogLevel: proto.UnitLogLevel_DEBUG, + AgentInfo: agentInfo, + Units: []*proto.UnitExpected{ + { + Id: "output-unit", + Type: proto.UnitType_OUTPUT, + ConfigStateIdx: 1, + State: proto.State_STOPPED, + LogLevel: proto.UnitLogLevel_INFO, + }, + { + Id: "input-unit-1", + Type: proto.UnitType_INPUT, + ConfigStateIdx: 1, + State: proto.State_STOPPED, + LogLevel: proto.UnitLogLevel_DEBUG, + }, + { + Id: "input-unit-2", + Type: proto.UnitType_INPUT, + ConfigStateIdx: 1, + State: proto.State_STOPPED, + LogLevel: proto.UnitLogLevel_INFO, + }, }, - { - Id: "input-unit-2", - Type: proto.UnitType_INPUT, - ConfigStateIdx: 1, - State: proto.State_STOPPED, - LogLevel: proto.UnitLogLevel_INFO, + Features: nil, + FeaturesIdx: 2, + Component: &proto.Component{ + ApmConfig: &proto.APMConfig{Elastic: apmConfig}, }, }, - {}, - }, - []uint64{1, 2, 2, 2}, - []*proto.Features{ - nil, - {Fqdn: &proto.FQDNFeature{Enabled: true}}, - nil, - nil, + { + AgentInfo: agentInfo, + Units: []*proto.UnitExpected{}, + Features: nil, + FeaturesIdx: 2, }, + }, onObserved, 500*time.Millisecond, ) From b14aa0c4831e7dec43838fbdd8ea077869ea309f Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Wed, 26 Jun 2024 10:56:53 -0400 Subject: [PATCH 3/5] Fix import. --- x-pack/libbeat/management/managerV2_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/libbeat/management/managerV2_test.go b/x-pack/libbeat/management/managerV2_test.go index 90063f82eeb..f1b32b15d82 100644 --- a/x-pack/libbeat/management/managerV2_test.go +++ b/x-pack/libbeat/management/managerV2_test.go @@ -7,7 +7,6 @@ package management import ( "errors" "fmt" - "github.com/elastic/beats/v7/libbeat/version" "sync" "sync/atomic" "testing" @@ -28,6 +27,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common/reload" "github.com/elastic/beats/v7/libbeat/features" "github.com/elastic/beats/v7/libbeat/tests/integration" + "github.com/elastic/beats/v7/libbeat/version" ) func TestManagerV2(t *testing.T) { From 1e194cb1423a4a6886dc380774b91d7955ec306c Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Wed, 26 Jun 2024 11:49:52 -0400 Subject: [PATCH 4/5] Fix issue from merge. --- x-pack/libbeat/management/managerV2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/libbeat/management/managerV2.go b/x-pack/libbeat/management/managerV2.go index a9e1d576fe4..fa7e0d397c3 100644 --- a/x-pack/libbeat/management/managerV2.go +++ b/x-pack/libbeat/management/managerV2.go @@ -879,7 +879,7 @@ func (cm *BeatV2Manager) reloadInputs(inputUnits []*agentUnit) error { // // In any other case, the bool is always false and the error will be non nil // if any error has occurred. -func (cm *BeatV2Manager) reloadAPM(unit *client.Unit) { +func (cm *BeatV2Manager) reloadAPM(unit *agentUnit) { // Assuming that the output reloadable isn't a list, see createBeater() in cmd/instance/beat.go apm := cm.registry.GetReloadableAPM() if apm == nil { From 9cc1b8cd6c3f7104d4297e6d5183a905a76e26c1 Mon Sep 17 00:00:00 2001 From: Blake Rouse Date: Fri, 28 Jun 2024 11:18:26 -0400 Subject: [PATCH 5/5] Update managerV2.go --- x-pack/libbeat/management/managerV2.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/x-pack/libbeat/management/managerV2.go b/x-pack/libbeat/management/managerV2.go index fa7e0d397c3..e39b394bf2b 100644 --- a/x-pack/libbeat/management/managerV2.go +++ b/x-pack/libbeat/management/managerV2.go @@ -873,12 +873,10 @@ func (cm *BeatV2Manager) reloadInputs(inputUnits []*agentUnit) error { return nil } -// reloadAPM reload APM tracing, it returns a bool and an error. -// The bool, if set, indicates that the output reload requires an restart, -// in that case the error is always `nil`. +// reloadAPM reloads APM tracing // -// In any other case, the bool is always false and the error will be non nil -// if any error has occurred. +// An error is not returned from this function, because in no case do we want APM trace configuration +// to cause the beat to fail. The error is logged appropriately in the case of a failure on reload. func (cm *BeatV2Manager) reloadAPM(unit *agentUnit) { // Assuming that the output reloadable isn't a list, see createBeater() in cmd/instance/beat.go apm := cm.registry.GetReloadableAPM()