From 12fe7deedb059f73abd40ca8d2988c6476ecfaf0 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Thu, 1 Apr 2021 14:11:12 +0200 Subject: [PATCH 1/4] restart on output change --- .../elastic-agent/pkg/agent/program/spec.go | 29 +++--- .../pkg/agent/program/supported.go | 2 +- .../elastic-agent/pkg/core/plugin/common.go | 79 ++++++++++++++++ .../pkg/core/plugin/common_test.go | 94 +++++++++++++++++++ .../pkg/core/plugin/process/configure.go | 17 +++- .../pkg/core/plugin/service/app.go | 20 +++- x-pack/elastic-agent/spec/filebeat.yml | 1 + x-pack/elastic-agent/spec/heartbeat.yml | 1 + x-pack/elastic-agent/spec/metricbeat.yml | 1 + x-pack/elastic-agent/spec/osquerybeat.yml | 3 +- x-pack/elastic-agent/spec/packetbeat.yml | 1 + 11 files changed, 227 insertions(+), 21 deletions(-) create mode 100644 x-pack/elastic-agent/pkg/core/plugin/common.go create mode 100644 x-pack/elastic-agent/pkg/core/plugin/common_test.go diff --git a/x-pack/elastic-agent/pkg/agent/program/spec.go b/x-pack/elastic-agent/pkg/agent/program/spec.go index 98e539d20077..79a08071202d 100644 --- a/x-pack/elastic-agent/pkg/agent/program/spec.go +++ b/x-pack/elastic-agent/pkg/agent/program/spec.go @@ -27,20 +27,21 @@ var ErrMissingWhen = errors.New("program must define a 'When' expression") // NOTE: Current spec are build at compile time, we want to revisit that to allow other program // to register their spec in a secure way. type Spec struct { - Name string `yaml:"name"` - ServicePort int `yaml:"service,omitempty"` - Cmd string `yaml:"cmd"` - Args []string `yaml:"args"` - Artifact string `yaml:"artifact"` - ActionInputTypes []string `yaml:"action_input_types,omitempty"` - LogPaths map[string]string `yaml:"log_paths,omitempty"` - MetricEndpoints map[string]string `yaml:"metric_endpoints,omitempty"` - Rules *transpiler.RuleList `yaml:"rules"` - CheckInstallSteps *transpiler.StepList `yaml:"check_install"` - PostInstallSteps *transpiler.StepList `yaml:"post_install"` - PreUninstallSteps *transpiler.StepList `yaml:"pre_uninstall"` - When string `yaml:"when"` - Constraints string `yaml:"constraints"` + Name string `yaml:"name"` + ServicePort int `yaml:"service,omitempty"` + Cmd string `yaml:"cmd"` + Args []string `yaml:"args"` + Artifact string `yaml:"artifact"` + ActionInputTypes []string `yaml:"action_input_types,omitempty"` + LogPaths map[string]string `yaml:"log_paths,omitempty"` + MetricEndpoints map[string]string `yaml:"metric_endpoints,omitempty"` + Rules *transpiler.RuleList `yaml:"rules"` + CheckInstallSteps *transpiler.StepList `yaml:"check_install"` + PostInstallSteps *transpiler.StepList `yaml:"post_install"` + PreUninstallSteps *transpiler.StepList `yaml:"pre_uninstall"` + When string `yaml:"when"` + Constraints string `yaml:"constraints"` + RestartOnOutputChange bool `yaml:"restart_on_output_change,omitempty"` } // ReadSpecs reads all the specs that match the provided globbing path. diff --git a/x-pack/elastic-agent/pkg/agent/program/supported.go b/x-pack/elastic-agent/pkg/agent/program/supported.go index c935f3ee6a6b..a27a803b4943 100644 --- a/x-pack/elastic-agent/pkg/agent/program/supported.go +++ b/x-pack/elastic-agent/pkg/agent/program/supported.go @@ -25,7 +25,7 @@ func init() { // spec/metricbeat.yml // spec/osquerybeat.yml // spec/packetbeat.yml - unpacked := packer.MustUnpack("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") + unpacked := packer.MustUnpack("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") SupportedMap = make(map[string]Spec) for f, v := range unpacked { diff --git a/x-pack/elastic-agent/pkg/core/plugin/common.go b/x-pack/elastic-agent/pkg/core/plugin/common.go new file mode 100644 index 000000000000..7bb25c7e94d0 --- /dev/null +++ b/x-pack/elastic-agent/pkg/core/plugin/common.go @@ -0,0 +1,79 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package plugin + +import ( + "gopkg.in/yaml.v2" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/config" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" +) + +type configFetcher interface { + Config() string +} + +// IsRestartNeeded returns true if +// - spec is configured to support restart on change +// - output changes in between configs +func IsRestartNeeded(log *logger.Logger, spec program.Spec, cfgFetch configFetcher, newCfg map[string]interface{}) bool { + // compare outputs + curCfgStr := cfgFetch.Config() + if curCfgStr == "" { + // no config currently applied + return false + } + + currentOutput, err := getOutputConfigFromString(curCfgStr) + if err != nil { + log.Errorf("failed to retrieve output config from current state: %v", err) + return false + } + + newOutput, err := getOutputConfigFromMap(newCfg) + if err != nil { + log.Errorf("failed to retrieve output config from new state: %v", err) + return false + } + + // restart needed only if specified and output changed + return spec.RestartOnOutputChange && currentOutput != newOutput +} + +func getOutputConfigFromString(cfgString string) (string, error) { + cfg, err := config.NewConfigFrom(cfgString) + if err != nil { + return "", err + } + + cfgMap, err := cfg.ToMapStr() + if err != nil { + return "", err + } + + return getOutputConfigFromMap(cfgMap) +} + +func getOutputConfigFromMap(cfgMap map[string]interface{}) (string, error) { + outputCfgIface, found := cfgMap["output"] + if !found { + // output not found not an error + return "", nil + } + + outputCfg, ok := outputCfgIface.(map[string]interface{}) + if !ok { + return "", errors.New("not a map") + } + + cfgStr, err := yaml.Marshal(outputCfg) + if err != nil { + return "", errors.New(err, errors.TypeApplication) + } + + return string(cfgStr), nil +} diff --git a/x-pack/elastic-agent/pkg/core/plugin/common_test.go b/x-pack/elastic-agent/pkg/core/plugin/common_test.go new file mode 100644 index 000000000000..10baadb6637f --- /dev/null +++ b/x-pack/elastic-agent/pkg/core/plugin/common_test.go @@ -0,0 +1,94 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License; +// you may not use this file except in compliance with the Elastic License. + +package plugin + +import ( + "testing" + + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" +) + +func TestRestartNeeded(t *testing.T) { + tt := []struct { + Name string + OldOutput map[string]interface{} + NewOutput map[string]interface{} + ShouldRestart bool + + ExpectedRestart bool + }{ + { + "same empty output", + map[string]interface{}{}, + map[string]interface{}{}, + true, + false, + }, + { + "same not empty output", + map[string]interface{}{"output": map[string]interface{}{"username": "user", "password": "123456"}}, + map[string]interface{}{"output": map[string]interface{}{"username": "user", "password": "123456"}}, + true, + false, + }, + { + "different empty output", + map[string]interface{}{}, + map[string]interface{}{"output": map[string]interface{}{"username": "user", "password": "123456"}}, + true, + false, + }, + { + "different not empty output", + map[string]interface{}{"output": map[string]interface{}{"username": "user", "password": "123456"}}, + map[string]interface{}{"output": map[string]interface{}{"username": "user", "password": "s3cur3_Pa55;"}}, + true, + true, + }, + { + "different not empty output no restart required", + map[string]interface{}{"output": map[string]interface{}{"username": "user", "password": "123456"}}, + map[string]interface{}{"output": map[string]interface{}{"username": "user", "password": "s3cur3_Pa55;"}}, + false, + false, + }, + } + + for _, tc := range tt { + t.Run(tc.Name, func(t *testing.T) { + cf, err := newTestConfigFetcher(tc.OldOutput) + require.NoError(t, err) + s := testProgramSpec(tc.ShouldRestart) + l, _ := logger.New("tst") + + IsRestartNeeded(l, s, cf, tc.NewOutput) + }) + } +} + +func newTestConfigFetcher(cfg map[string]interface{}) (*testConfigFetcher, error) { + cfgStr, err := yaml.Marshal(cfg) + if err != nil { + return nil, errors.New(err, errors.TypeApplication) + } + + return &testConfigFetcher{cfg: string(cfgStr)}, nil +} + +type testConfigFetcher struct { + cfg string +} + +func (f testConfigFetcher) Config() string { return f.cfg } + +func testProgramSpec(restartOnOutput bool) program.Spec { + return program.Spec{ + RestartOnOutputChange: restartOnOutput, + } +} diff --git a/x-pack/elastic-agent/pkg/core/plugin/process/configure.go b/x-pack/elastic-agent/pkg/core/plugin/process/configure.go index 23ebdafbf60d..56f0211c9c01 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/process/configure.go +++ b/x-pack/elastic-agent/pkg/core/plugin/process/configure.go @@ -10,11 +10,12 @@ import ( "gopkg.in/yaml.v2" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/state" ) // Configure configures the application with the passed configuration. -func (a *Application) Configure(_ context.Context, config map[string]interface{}) (err error) { +func (a *Application) Configure(ctx context.Context, config map[string]interface{}) (err error) { defer func() { if err != nil { // inject App metadata @@ -37,10 +38,22 @@ func (a *Application) Configure(_ context.Context, config map[string]interface{} if err != nil { return errors.New(err, errors.TypeApplication) } + + isRestartNeeded := plugin.IsRestartNeeded(a.logger, a.Spec(), a.srvState, config) + err = a.srvState.UpdateConfig(string(cfgStr)) if err != nil { return errors.New(err, errors.TypeApplication) } - return nil + if isRestartNeeded { + a.logger.Infof("initiating restart of '%s' due to config change", a.Name()) + a.appLock.Unlock() + a.Stop() + err = a.Start(ctx, a.desc, config) + // lock back so it wont panic on deferred unlock + a.appLock.Lock() + } + + return err } diff --git a/x-pack/elastic-agent/pkg/core/plugin/service/app.go b/x-pack/elastic-agent/pkg/core/plugin/service/app.go index b33561f305f5..4ccfec902da2 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/service/app.go +++ b/x-pack/elastic-agent/pkg/core/plugin/service/app.go @@ -24,6 +24,7 @@ import ( "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/app" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/monitoring" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/plugin" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/process" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/server" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/state" @@ -145,7 +146,7 @@ func (a *Application) SetState(s state.Status, msg string, payload map[string]in } // Start starts the application with a specified config. -func (a *Application) Start(ctx context.Context, t app.Taggable, cfg map[string]interface{}) (err error) { +func (a *Application) Start(ctx context.Context, _ app.Taggable, cfg map[string]interface{}) (err error) { defer func() { if err != nil { // inject App metadata @@ -203,7 +204,7 @@ func (a *Application) Start(ctx context.Context, t app.Taggable, cfg map[string] } // Configure configures the application with the passed configuration. -func (a *Application) Configure(_ context.Context, config map[string]interface{}) (err error) { +func (a *Application) Configure(ctx context.Context, config map[string]interface{}) (err error) { defer func() { if err != nil { // inject App metadata @@ -223,11 +224,24 @@ func (a *Application) Configure(_ context.Context, config map[string]interface{} if err != nil { return errors.New(err, errors.TypeApplication) } + + isRestartNeeded := plugin.IsRestartNeeded(a.logger, a.Spec(), a.srvState, config) + err = a.srvState.UpdateConfig(string(cfgStr)) if err != nil { return errors.New(err, errors.TypeApplication) } - return nil + + if isRestartNeeded { + a.logger.Infof("initiating restart of '%s' due to config change", a.Name()) + a.appLock.Unlock() + a.Stop() + err = a.Start(ctx, a.desc, config) + // lock back so it wont panic on deferred unlock + a.appLock.Lock() + } + + return err } // Stop stops the current application. diff --git a/x-pack/elastic-agent/spec/filebeat.yml b/x-pack/elastic-agent/spec/filebeat.yml index 6aacf99ccf0d..9aa4d655f535 100644 --- a/x-pack/elastic-agent/spec/filebeat.yml +++ b/x-pack/elastic-agent/spec/filebeat.yml @@ -2,6 +2,7 @@ name: Filebeat cmd: filebeat args: ["-E", "setup.ilm.enabled=false", "-E", "setup.template.enabled=false", "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true", "-E", "logging.level=debug"] artifact: beats/filebeat +restart_on_output_change: true rules: - fix_stream: {} - inject_index: diff --git a/x-pack/elastic-agent/spec/heartbeat.yml b/x-pack/elastic-agent/spec/heartbeat.yml index 399fd7d08851..b4f5b14e5a87 100644 --- a/x-pack/elastic-agent/spec/heartbeat.yml +++ b/x-pack/elastic-agent/spec/heartbeat.yml @@ -2,6 +2,7 @@ name: Heartbeat cmd: heartbeat args: ["-E", "setup.ilm.enabled=false", "-E", "setup.template.enabled=false", "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true", "-E", "logging.level=debug"] artifact: beats/heartbeat +restart_on_output_change: true rules: - fix_stream: {} - filter_values_with_regexp: diff --git a/x-pack/elastic-agent/spec/metricbeat.yml b/x-pack/elastic-agent/spec/metricbeat.yml index a5015a974a57..b06575014d77 100644 --- a/x-pack/elastic-agent/spec/metricbeat.yml +++ b/x-pack/elastic-agent/spec/metricbeat.yml @@ -2,6 +2,7 @@ name: Metricbeat cmd: metricbeat args: ["-E", "setup.ilm.enabled=false", "-E", "setup.template.enabled=false", "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true", "-E", "logging.level=debug"] artifact: beats/metricbeat +restart_on_output_change: true post_install: - move_file: path: "modules.d/system.yml" diff --git a/x-pack/elastic-agent/spec/osquerybeat.yml b/x-pack/elastic-agent/spec/osquerybeat.yml index 8a7dd8b35389..c9b583d20479 100644 --- a/x-pack/elastic-agent/spec/osquerybeat.yml +++ b/x-pack/elastic-agent/spec/osquerybeat.yml @@ -1,6 +1,7 @@ name: Osquerybeat cmd: osquerybeat args: ["-E", "setup.ilm.enabled=false", "-E", "setup.template.enabled=false", "-E", "management.mode=x-pack-fleet", "-E", "management.enabled=true", "-E", "logging.level=debug"] +restart_on_output_change: true action_input_types: - osquery @@ -25,4 +26,4 @@ rules: - output when: length(${inputs}) > 0 and hasKey(${output}, 'elasticsearch') -constraints: ${runtime.arch} != 'arm64' \ No newline at end of file +constraints: ${runtime.arch} != 'arm64' diff --git a/x-pack/elastic-agent/spec/packetbeat.yml b/x-pack/elastic-agent/spec/packetbeat.yml index 6dffecbda6df..bf05f901c48f 100644 --- a/x-pack/elastic-agent/spec/packetbeat.yml +++ b/x-pack/elastic-agent/spec/packetbeat.yml @@ -2,6 +2,7 @@ name: Packetbeat cmd: packetbeat args: ['-E', 'setup.ilm.enabled=false', '-E', 'setup.template.enabled=false', '-E', 'management.mode=x-pack-fleet', '-E', 'management.enabled=true', '-E', 'logging.level=debug'] artifact: beats/packetbeat +restart_on_output_change: true rules: - filter_values: selector: inputs From 90e41fbff54a2aa182a38bb1b9b48a9d7ef88ae7 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Thu, 1 Apr 2021 17:54:19 +0200 Subject: [PATCH 2/4] changelog + fix --- x-pack/elastic-agent/CHANGELOG.asciidoc | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/x-pack/elastic-agent/CHANGELOG.asciidoc b/x-pack/elastic-agent/CHANGELOG.asciidoc index 2b67153634fb..16e9fd671ce5 100644 --- a/x-pack/elastic-agent/CHANGELOG.asciidoc +++ b/x-pack/elastic-agent/CHANGELOG.asciidoc @@ -34,15 +34,15 @@ - Fixed Monitoring filebeat and metricbeat not connecting to Agent over GRPC {pull}23843[23843] - Fixed make status readable in the log. {pull}23849[23849] - Windows agent doesn't uninstall with a lowercase `c:` drive in the path {pull}23998[23998] -- Fix reloading of log level for services {pull}[24055]24055 -- Fix: Successfully installed and enrolled agent running standalone{pull}[24128]24128 -- Make installer atomic on windows {pull}[24253]24253 -- Remove installed services on agent uninstall {pull}[24151]24151 -- Fix failing installation on windows 7 {pull}[24387]24387 -- Fix capabilities resolution in inspect command {pull}[24346]24346 -- Fix windows installer during enroll {pull}[24343]24343 -- Logging to file disabled on enroll {issue}[24173]24173 -- Prevent uninstall failures on empty config {pull}[24838]24838 +- Fix reloading of log level for services {pull}24055[24055] +- Fix: Successfully installed and enrolled agent running standalone{pull}24128[24128] +- Make installer atomic on windows {pull}24253[24253] +- Remove installed services on agent uninstall {pull}24151[24151] +- Fix failing installation on windows 7 {pull}24387[24387] +- Fix capabilities resolution in inspect command {pull}24346[24346] +- Fix windows installer during enroll {pull}24343[24343] +- Logging to file disabled on enroll {issue}24173[24173] +- Prevent uninstall failures on empty config {pull}24838[24838] ==== New features @@ -63,6 +63,7 @@ - Add `event.dataset` to all events {pull}20076[20076] - Send datastreams fields {pull}20416[20416] - Agent supports capabilities definition {pull}23848[23848] +- Restart process on output change {pull}24907[24907] [[release-notes-7.8.0]] === Elastic Agent version 7.8.0 From d6146f9f7ee6d9ab4fbefee1e684233716e093b9 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Thu, 1 Apr 2021 18:07:38 +0200 Subject: [PATCH 3/4] fix --- x-pack/elastic-agent/pkg/core/plugin/common_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/elastic-agent/pkg/core/plugin/common_test.go b/x-pack/elastic-agent/pkg/core/plugin/common_test.go index 10baadb6637f..4fa7243ae3b5 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/common_test.go +++ b/x-pack/elastic-agent/pkg/core/plugin/common_test.go @@ -65,7 +65,7 @@ func TestRestartNeeded(t *testing.T) { cf, err := newTestConfigFetcher(tc.OldOutput) require.NoError(t, err) s := testProgramSpec(tc.ShouldRestart) - l, _ := logger.New("tst") + l, _ := logger.New("tst", false) IsRestartNeeded(l, s, cf, tc.NewOutput) }) From fc3a2d14563cd3995a580c777718781ffcda8810 Mon Sep 17 00:00:00 2001 From: Michal Pristas Date: Tue, 6 Apr 2021 09:46:59 +0200 Subject: [PATCH 4/4] fmt --- x-pack/elastic-agent/pkg/core/plugin/common_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/elastic-agent/pkg/core/plugin/common_test.go b/x-pack/elastic-agent/pkg/core/plugin/common_test.go index 4fa7243ae3b5..70b673e09ad2 100644 --- a/x-pack/elastic-agent/pkg/core/plugin/common_test.go +++ b/x-pack/elastic-agent/pkg/core/plugin/common_test.go @@ -7,11 +7,12 @@ package plugin import ( "testing" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" + "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/errors" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/agent/program" "github.com/elastic/beats/v7/x-pack/elastic-agent/pkg/core/logger" - "github.com/stretchr/testify/require" - "gopkg.in/yaml.v2" ) func TestRestartNeeded(t *testing.T) {