diff --git a/NOTICE.txt b/NOTICE.txt index 62247e9a121..0a84c783555 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -6139,6 +6139,44 @@ Contents of probable licence file $GOMODCACHE/github.com/spf13/cobra@v1.8.0/LICE of your accepting any such warranty or additional liability. +-------------------------------------------------------------------------------- +Dependency : github.com/spf13/pflag +Version: v1.0.5 +Licence type (autodetected): BSD-3-Clause +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/spf13/pflag@v1.0.5/LICENSE: + +Copyright (c) 2012 Alex Ogier. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + -------------------------------------------------------------------------------- Dependency : github.com/stretchr/testify Version: v1.8.4 @@ -26663,44 +26701,6 @@ Contents of probable licence file $GOMODCACHE/github.com/spf13/afero@v1.9.5/LICE of your accepting any such warranty or additional liability. --------------------------------------------------------------------------------- -Dependency : github.com/spf13/pflag -Version: v1.0.5 -Licence type (autodetected): BSD-3-Clause --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/spf13/pflag@v1.0.5/LICENSE: - -Copyright (c) 2012 Alex Ogier. All rights reserved. -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -------------------------------------------------------------------------------- Dependency : github.com/stretchr/objx Version: v0.5.0 diff --git a/go.mod b/go.mod index 454ac18fa2c..33179465bd2 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.12 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.0 + github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 github.com/tsg/go-daemon v0.0.0-20200207173439-e704b93fd89b go.elastic.co/apm/module/apmgorilla v1.15.0 @@ -187,7 +188,6 @@ require ( github.com/sergi/go-diff v1.2.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect diff --git a/internal/pkg/agent/application/application.go b/internal/pkg/agent/application/application.go index c5619e5293b..e2394b0a1be 100644 --- a/internal/pkg/agent/application/application.go +++ b/internal/pkg/agent/application/application.go @@ -134,6 +134,9 @@ func New( // testing mode uses a config manager that takes configuration from over the control protocol configMgr = newTestingModeConfigManager(log) + } else if runAsOtel { + // ignoring configuration in elastic-agent.yml + configMgr = otel.NewOtelModeConfigManager() } else if configuration.IsStandalone(cfg.Fleet) { log.Info("Parsed configuration and determined agent is managed locally") @@ -146,9 +149,6 @@ func New( log.Debugf("Reloading of configuration is on, frequency is set to %s", cfg.Settings.Reload.Period) configMgr = newPeriodic(log, cfg.Settings.Reload.Period, discover, loader) } - } else if runAsOtel { - // ignoring configuration in elastic-agent.yml - configMgr = otel.NewOtelModeConfigManager() } else { isManaged = true var store storage.Store diff --git a/internal/pkg/agent/application/paths/common.go b/internal/pkg/agent/application/paths/common.go index 5a2a458744d..5a90712744a 100644 --- a/internal/pkg/agent/application/paths/common.go +++ b/internal/pkg/agent/application/paths/common.go @@ -24,6 +24,8 @@ import ( const ( // DefaultConfigName is the default name of the configuration file. DefaultConfigName = "elastic-agent.yml" + // DefaultOtelConfigName is the default name of the otel configuration file. + DefaultOtelConfigName = "otel.yml" // AgentLockFileName is the name of the overall Elastic Agent file lock. AgentLockFileName = "agent.lock" @@ -151,8 +153,19 @@ func SetConfig(path string) { // ConfigFile returns the path to the configuration file. func ConfigFile() string { + + return configFileWithDefaultOverride(DefaultConfigName) +} + +// OtelConfigFile returns the path to the otel configuration file. +func OtelConfigFile() string { + return configFileWithDefaultOverride(DefaultOtelConfigName) +} + +// configFileWithDefaultOverride returns the path to the configuration file overriding default value. +func configFileWithDefaultOverride(defaultConfig string) string { if configFilePath == "" || configFilePath == DefaultConfigName { - return filepath.Join(Config(), DefaultConfigName) + return filepath.Join(Config(), defaultConfig) } if filepath.IsAbs(configFilePath) { return configFilePath diff --git a/internal/pkg/agent/cmd/common.go b/internal/pkg/agent/cmd/common.go index 7f106ab9e92..2837b62ab59 100644 --- a/internal/pkg/agent/cmd/common.go +++ b/internal/pkg/agent/cmd/common.go @@ -81,6 +81,7 @@ func NewCommandWithArgs(args []string, streams *cli.IOStreams) *cobra.Command { cmd.AddCommand(newDiagnosticsCommand(args, streams)) cmd.AddCommand(newComponentCommandWithArgs(args, streams)) cmd.AddCommand(newLogsCommandWithArgs(args, streams)) + cmd.AddCommand(newOtelCommandWithArgs(args, streams)) // windows special hidden sub-command (only added on Windows) reexec := newReExecWindowsCommand(args, streams) diff --git a/internal/pkg/agent/cmd/otel.go b/internal/pkg/agent/cmd/otel.go new file mode 100644 index 00000000000..8e57d428107 --- /dev/null +++ b/internal/pkg/agent/cmd/otel.go @@ -0,0 +1,125 @@ +// 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 cmd + +import ( + "context" + "sync" + + "github.com/hashicorp/go-multierror" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/elastic/elastic-agent-libs/service" + "github.com/elastic/elastic-agent/internal/pkg/agent/errors" + "github.com/elastic/elastic-agent/internal/pkg/cli" + "github.com/elastic/elastic-agent/internal/pkg/otel" +) + +const ( + configFlagName = "config" + setFlagName = "set" +) + +func newOtelCommandWithArgs(_ []string, _ *cli.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "otel", + Short: "Start the Elastic Agent in otel mode", + Long: "This command starts the Elastic Agent in otel mode.", + RunE: func(cmd *cobra.Command, _ []string) error { + cfgFiles, err := getConfigFiles(cmd) + if err != nil { + return err + } + return runCollector(cmd.Context(), cfgFiles) + }, + PreRun: func(c *cobra.Command, args []string) { + // hide inherited flags not to bloat help with flags not related to otel + hideInheritedFlags(c) + }, + SilenceUsage: true, + SilenceErrors: true, + } + + cmd.SetHelpFunc(func(c *cobra.Command, s []string) { + hideInheritedFlags(c) + c.Parent().HelpFunc()(c, s) + }) + + cmd.Flags().StringArray(configFlagName, []string{}, "Locations to the config file(s), note that only a"+ + " single location can be set per flag entry e.g. `--config=file:/path/to/first --config=file:path/to/second`.") + + cmd.Flags().StringArray(setFlagName, []string{}, "Set arbitrary component config property. The component has to be defined in the config file and the flag"+ + " has a higher precedence. Array config properties are overridden and maps are joined. Example --set=processors.batch.timeout=2s") + return cmd +} + +func hideInheritedFlags(c *cobra.Command) { + c.InheritedFlags().VisitAll(func(f *pflag.Flag) { + f.Hidden = true + }) +} + +func runCollector(cmdCtx context.Context, configFiles []string) error { + // Windows: Mark service as stopped. + // After this is run, the service is considered by the OS to be stopped. + // This must be the first deferred cleanup task (last to execute). + defer func() { + service.NotifyTermination() + service.WaitExecutionDone() + }() + + service.BeforeRun() + defer service.Cleanup() + + stop := make(chan bool) + ctx, cancel := context.WithCancel(cmdCtx) + + var stopCollector = func() { + close(stop) + } + + defer cancel() + go service.ProcessWindowsControlEvents(stopCollector) + + var otelStartWg sync.WaitGroup + var resErr error + var awaiters awaiters + + otelAwaiter := make(chan struct{}) + awaiters = append(awaiters, otelAwaiter) + + otelStartWg.Add(1) + go func() { + otelStartWg.Done() + if err := otel.Run(ctx, stop, configFiles); err != nil { + resErr = multierror.Append(resErr, err) + // otel collector finished with an error, exit run loop + cancel() + } + + // close awaiter handled in run loop + close(otelAwaiter) + }() + + // wait for otel to start + otelStartWg.Wait() + + if err := runElasticAgent( + ctx, + cancel, + nil, // no config overrides + stop, // service hook + false, // not in testing mode + 0, // no fleet config + true, // is otel mode + awaiters, // wait for otel to finish + ); err != nil && !errors.Is(err, context.Canceled) { + resErr = multierror.Append(resErr, err) + } + + return resErr + +} diff --git a/internal/pkg/agent/cmd/otel_flags.go b/internal/pkg/agent/cmd/otel_flags.go new file mode 100644 index 00000000000..432aaf047ce --- /dev/null +++ b/internal/pkg/agent/cmd/otel_flags.go @@ -0,0 +1,57 @@ +// 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 cmd + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" +) + +func getConfigFiles(cmd *cobra.Command) ([]string, error) { + configFiles, err := cmd.Flags().GetStringArray(configFlagName) + if err != nil { + return nil, fmt.Errorf("failed to retrieve config flags: %w", err) + } + + if len(configFiles) == 0 { + configFiles = append(configFiles, paths.OtelConfigFile()) + } + + setVals, err := cmd.Flags().GetStringArray(setFlagName) + if err != nil { + return nil, fmt.Errorf("failed to retrieve set flags: %w", err) + } + + sets, err := getSets(setVals) + if err != nil { + return nil, err + } + + configFiles = append(configFiles, sets...) + return configFiles, nil +} + +func getSets(setVals []string) ([]string, error) { + var sets []string + for _, s := range setVals { + idx := strings.Index(s, "=") + if idx == -1 { + return nil, fmt.Errorf("missing equal sign for set value %q", s) + } + sets = append(sets, setToYaml(s, idx)) + } + return sets, nil +} + +func setToYaml(set string, eqIdx int) string { + if len(set) == 0 { + return set + } + return "yaml:" + strings.TrimSpace(strings.ReplaceAll(set[:eqIdx], ".", "::")) + ": " + strings.TrimSpace(set[eqIdx+1:]) +} diff --git a/internal/pkg/agent/cmd/otel_flags_test.go b/internal/pkg/agent/cmd/otel_flags_test.go new file mode 100644 index 00000000000..65eee9d7ada --- /dev/null +++ b/internal/pkg/agent/cmd/otel_flags_test.go @@ -0,0 +1,97 @@ +// 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 cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetSets(t *testing.T) { + testCases := []struct { + name string + args []string + expectedSets []string + expectedError string + }{ + { + name: "No Set", + args: []string{}, + expectedSets: nil, + expectedError: "", + }, + { + name: "Valid Set", + args: []string{"key=value"}, + expectedSets: []string{"yaml:key: value"}, + expectedError: "", + }, + { + name: "Valid Multiple Set", + args: []string{"key=value", "key2=value2"}, + expectedSets: []string{"yaml:key: value", "yaml:key2: value2"}, + expectedError: "", + }, + { + name: "Invalid Set", + args: []string{"keyvalue"}, + expectedSets: nil, + expectedError: "missing equal sign for set value \"keyvalue\"", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sets, err := getSets(tc.args) + if len(tc.expectedError) > 0 { + assert.Contains(t, err.Error(), tc.expectedError) + } else { + require.NoError(t, err) + } + assert.Equal(t, tc.expectedSets, sets) + }) + } +} + +func TestSetToYaml(t *testing.T) { + testCases := []struct { + name string + set string + idx int + expectedSet string + }{ + { + name: "Empty set", + set: "", + idx: 0, + expectedSet: "", + }, + { + name: "Simple set", + set: "key=value", + idx: 3, + expectedSet: "yaml:key: value", + }, + { + name: "Dotted key", + set: "key.subkey=value", + idx: 10, + expectedSet: "yaml:key::subkey: value", + }, + { + name: "Dotted value", + set: "key=value.somethingelse", + idx: 3, + expectedSet: "yaml:key: value.somethingelse", + }, + } + + for _, tc := range testCases { + actualSet := setToYaml(tc.set, tc.idx) + assert.Equal(t, tc.expectedSet, actualSet) + } +} diff --git a/internal/pkg/agent/cmd/run.go b/internal/pkg/agent/cmd/run.go index e878e75bb98..1c44dfc79b3 100644 --- a/internal/pkg/agent/cmd/run.go +++ b/internal/pkg/agent/cmd/run.go @@ -12,7 +12,6 @@ import ( "os/signal" "path/filepath" "strings" - "sync" "syscall" "time" @@ -20,7 +19,6 @@ import ( apmtransport "go.elastic.co/apm/transport" "gopkg.in/yaml.v2" - "github.com/hashicorp/go-multierror" "github.com/spf13/cobra" "github.com/elastic/elastic-agent-libs/api" @@ -48,7 +46,6 @@ import ( "github.com/elastic/elastic-agent/internal/pkg/config" monitoringCfg "github.com/elastic/elastic-agent/internal/pkg/core/monitoring/config" "github.com/elastic/elastic-agent/internal/pkg/diagnostics" - "github.com/elastic/elastic-agent/internal/pkg/otel" "github.com/elastic/elastic-agent/internal/pkg/release" "github.com/elastic/elastic-agent/pkg/component" "github.com/elastic/elastic-agent/pkg/control/v2/server" @@ -138,35 +135,7 @@ func run(override cfgOverrider, testingMode bool, fleetInitTimeout time.Duration defer cancel() go service.ProcessWindowsControlEvents(stopBeat) - // detect otel - runAsOtel := otel.IsOtelConfig(ctx, paths.ConfigFile()) - var awaiters awaiters - var resErr error - if runAsOtel { - var otelStartWg sync.WaitGroup - otelAwaiter := make(chan struct{}) - awaiters = append(awaiters, otelAwaiter) - - otelStartWg.Add(1) - go func() { - otelStartWg.Done() - if err := otel.Run(ctx, stop); err != nil { - resErr = multierror.Append(resErr, err) - } - - // close awaiter handled in run loop - close(otelAwaiter) - }() - - // wait for otel to start - otelStartWg.Wait() - } - - if err := runElasticAgent(ctx, cancel, override, stop, testingMode, fleetInitTimeout, runAsOtel, awaiters, modifiers...); err != nil { - resErr = multierror.Append(resErr, err) - } - - return resErr + return runElasticAgent(ctx, cancel, override, stop, testingMode, fleetInitTimeout, false, nil, modifiers...) } func runElasticAgent(ctx context.Context, cancel context.CancelFunc, override cfgOverrider, stop chan bool, testingMode bool, fleetInitTimeout time.Duration, runAsOtel bool, awaiters awaiters, modifiers ...component.PlatformModifier) error { @@ -208,7 +177,7 @@ func runElasticAgent(ctx context.Context, cancel context.CancelFunc, override cf } // agent ID needs to stay empty in bootstrap mode - createAgentID := true + createAgentID := !runAsOtel if cfg.Fleet != nil && cfg.Fleet.Server != nil && cfg.Fleet.Server.Bootstrap { createAgentID = false } @@ -405,7 +374,12 @@ LOOP: func loadConfig(ctx context.Context, override cfgOverrider, runAsOtel bool) (*configuration.Configuration, error) { if runAsOtel { - return configuration.DefaultConfiguration(), nil + defaultCfg := configuration.DefaultConfiguration() + // disable monitoring to avoid injection of monitoring components + // in case inputs are not empty + defaultCfg.Settings.MonitoringConfig.Enabled = false + defaultCfg.Settings.V1MonitoringEnabled = false + return defaultCfg, nil } pathConfigFile := paths.ConfigFile() diff --git a/internal/pkg/otel/config_manager.go b/internal/pkg/otel/config_manager.go index a9a90cab627..44f4cf00977 100644 --- a/internal/pkg/otel/config_manager.go +++ b/internal/pkg/otel/config_manager.go @@ -8,6 +8,7 @@ import ( "context" "github.com/elastic/elastic-agent/internal/pkg/agent/application/coordinator" + "github.com/elastic/elastic-agent/internal/pkg/config" ) // OtelModeConfigManager serves as a config manager for OTel use cases @@ -28,6 +29,11 @@ func NewOtelModeConfigManager() *OtelModeConfigManager { } func (t *OtelModeConfigManager) Run(ctx context.Context) error { + // send config to transition from STARTING to HEALTHY + select { + case t.ch <- &otelConfigChange{}: + case <-ctx.Done(): + } <-ctx.Done() return ctx.Err() } @@ -45,3 +51,19 @@ func (t *OtelModeConfigManager) ActionErrors() <-chan error { func (t *OtelModeConfigManager) Watch() <-chan coordinator.ConfigChange { return t.ch } + +type otelConfigChange struct { +} + +func (l *otelConfigChange) Config() *config.Config { + return config.New() +} + +func (l *otelConfigChange) Ack() error { + // do nothing + return nil +} + +func (l *otelConfigChange) Fail(_ error) { + // do nothing +} diff --git a/internal/pkg/otel/run.go b/internal/pkg/otel/run.go index 833121221d0..ba1e2c175ee 100644 --- a/internal/pkg/otel/run.go +++ b/internal/pkg/otel/run.go @@ -8,44 +8,25 @@ import ( "context" "fmt" "os" - "path/filepath" - "strings" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/confmap/converter/expandconverter" + "go.opentelemetry.io/collector/confmap/provider/envprovider" + "go.opentelemetry.io/collector/confmap/provider/fileprovider" + "go.opentelemetry.io/collector/confmap/provider/httpprovider" + "go.opentelemetry.io/collector/confmap/provider/httpsprovider" + "go.opentelemetry.io/collector/confmap/provider/yamlprovider" "go.opentelemetry.io/collector/otelcol" - "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" "github.com/elastic/elastic-agent/internal/pkg/release" ) const buildDescription = "Elastic opentelemetry-collector distribution" -// IsOtelConfig returns true if file is named: -// - otel.(yaml|yml) -// - otlp.(yaml|yml) -// - otelcol.(yaml|yml) -// -// In other cases it returns false assuming agent config for backwards compatibility -func IsOtelConfig(ctx context.Context, pathConfigFile string) bool { - fileName := filepath.Base(pathConfigFile) - if suffix := filepath.Ext(fileName); suffix != ".yml" && suffix != ".yaml" { - return false - } - - cleanFileName := strings.TrimSpace(strings.ToLower(strings.TrimSuffix(fileName, filepath.Ext(fileName)))) - if cleanFileName == "otel" || cleanFileName == "otlp" || cleanFileName == "otelcol" { - return true - } - - // default behavior is Elastic Agent - return false -} - -func Run(ctx context.Context, stop chan bool) error { +func Run(ctx context.Context, stop chan bool, configFiles []string) error { fmt.Fprintln(os.Stdout, "Starting in otel mode") - settings, err := newSettings(paths.ConfigFile(), release.Version()) + settings, err := newSettings(release.Version(), configFiles) if err != nil { return err } @@ -66,18 +47,16 @@ func Run(ctx context.Context, stop chan bool) error { return svc.Run(cancelCtx) } -func newSettings(configPath string, version string) (*otelcol.CollectorSettings, error) { +func newSettings(version string, configPaths []string) (*otelcol.CollectorSettings, error) { buildInfo := component.BuildInfo{ Command: os.Args[0], Description: buildDescription, Version: version, } - - fmp := NewFileProviderWithDefaults() configProviderSettings := otelcol.ConfigProviderSettings{ ResolverSettings: confmap.ResolverSettings{ - URIs: []string{configPath}, - Providers: map[string]confmap.Provider{fmp.Scheme(): fmp}, + URIs: configPaths, + Providers: makeMapProvidersMap(fileprovider.New(), envprovider.New(), yamlprovider.New(), httpprovider.New(), httpsprovider.New()), Converters: []confmap.Converter{expandconverter.New()}, }, } @@ -95,3 +74,11 @@ func newSettings(configPath string, version string) (*otelcol.CollectorSettings, DisableGracefulShutdown: true, }, nil } + +func makeMapProvidersMap(providers ...confmap.Provider) map[string]confmap.Provider { + ret := make(map[string]confmap.Provider, len(providers)) + for _, provider := range providers { + ret[provider.Scheme()] = provider + } + return ret +} diff --git a/internal/pkg/otel/run_test.go b/internal/pkg/otel/run_test.go deleted file mode 100644 index 81e24ed05f1..00000000000 --- a/internal/pkg/otel/run_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// 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 otel - -import ( - "context" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestIsOtelConfig(t *testing.T) { - testCases := []struct { - name string - path string - expectedResult bool - }{ - // otel name based - {"named otel.yml", filepath.Join("testdata", "otel", "otel.yml"), true}, - {"named otel.yaml", filepath.Join("testdata", "otel", "otel.yaml"), true}, - {"named otlp.yml", filepath.Join("testdata", "otel", "otlp.yml"), true}, - {"named otelcol.yml", filepath.Join("testdata", "otel", "otelcol.yml"), true}, - - {"otel but wrong extension", filepath.Join("testdata", "otel", "otelcol.json"), false}, - {"wrong filename", filepath.Join("testdata", "otel", "elastic-agent.yml"), false}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - res := IsOtelConfig(context.TODO(), tc.path) - require.Equal(t, tc.expectedResult, res) - }) - } -} diff --git a/pkg/testing/fixture.go b/pkg/testing/fixture.go index 7a1b45ee49a..2f4a8da7518 100644 --- a/pkg/testing/fixture.go +++ b/pkg/testing/fixture.go @@ -429,7 +429,7 @@ func RunProcess(t *testing.T, } } -// RunWithClient runs the provided binary. +// RunOtelWithClient runs the provided binary in otel mode. // // If `states` are provided, agent runs until each state has been reached. Once reached the // Elastic Agent is stopped. If at any time the Elastic Agent logs an error log and the Fixture is not started @@ -445,7 +445,11 @@ func RunProcess(t *testing.T, // when `Run` is called. // // if shouldWatchState is set to false, communicating state does not happen. -func (f *Fixture) RunWithClient(ctx context.Context, shouldWatchState bool, enableTestingMode bool, states ...State) error { +func (f *Fixture) RunOtelWithClient(ctx context.Context, shouldWatchState bool, enableTestingMode bool, states ...State) error { + return f.executeWithClient(ctx, "otel", false, shouldWatchState, enableTestingMode, states...) +} + +func (f *Fixture) executeWithClient(ctx context.Context, command string, disableEncryptedStore bool, shouldWatchState bool, enableTestingMode bool, states ...State) error { if _, deadlineSet := ctx.Deadline(); !deadlineSet { f.t.Fatal("Context passed to Fixture.Run() has no deadline set.") } @@ -491,7 +495,10 @@ func (f *Fixture) RunWithClient(ctx context.Context, shouldWatchState bool, enab stdOut := newLogWatcher(logProxy) stdErr := newLogWatcher(logProxy) - args := []string{"run", "-e", "--disable-encrypted-store"} + args := []string{command, "-e"} + if disableEncryptedStore { + args = append(args, "--disable-encrypted-store") + } if enableTestingMode { args = append(args, "--testing-mode") } @@ -604,7 +611,7 @@ func (f *Fixture) RunWithClient(ctx context.Context, shouldWatchState bool, enab // The `elastic-agent.yml` generated by `Fixture.Configure` is ignored // when `Run` is called. func (f *Fixture) Run(ctx context.Context, states ...State) error { - return f.RunWithClient(ctx, true, true, states...) + return f.executeWithClient(ctx, "run", true, true, true, states...) } // Exec provides a way of performing subcommand on the prepared Elastic Agent binary. diff --git a/testing/integration/otel_test.go b/testing/integration/otel_test.go index 6aa06c9031e..af0c83f9f41 100644 --- a/testing/integration/otel_test.go +++ b/testing/integration/otel_test.go @@ -112,7 +112,7 @@ func TestOtelFileProcessing(t *testing.T) { cfgFilePath := filepath.Join(tempDir, "otel.yml") require.NoError(t, os.WriteFile(cfgFilePath, []byte(fileProcessingConfig), 0600)) - fixture, err := define.NewFixture(t, define.Version(), aTesting.WithAdditionalArgs([]string{"-c", cfgFilePath})) + fixture, err := define.NewFixture(t, define.Version(), aTesting.WithAdditionalArgs([]string{"--config", cfgFilePath})) require.NoError(t, err) ctx, cancel := testcontext.WithDeadline(t, context.Background(), time.Now().Add(10*time.Minute)) @@ -124,7 +124,7 @@ func TestOtelFileProcessing(t *testing.T) { fixtureWg.Add(1) go func() { defer fixtureWg.Done() - err = fixture.RunWithClient(ctx, false, false) + err = fixture.RunOtelWithClient(ctx, false, false) }() var content []byte @@ -141,7 +141,7 @@ func TestOtelFileProcessing(t *testing.T) { // the agent logs will be present in the error message // which should help to explain why the agent was not // healthy. - err := fixture.IsHealthy(ctx) + err = fixture.IsHealthy(ctx) return err == nil }, 2*time.Minute, time.Second, @@ -203,7 +203,7 @@ func TestOtelAPMIngestion(t *testing.T) { require.NoError(t, os.WriteFile(cfgFilePath, []byte(apmConfig), 0600)) require.NoError(t, os.WriteFile(filepath.Join(tempDir, fileName), []byte{}, 0600)) - fixture, err := define.NewFixture(t, define.Version(), aTesting.WithAdditionalArgs([]string{"-c", cfgFilePath})) + fixture, err := define.NewFixture(t, define.Version(), aTesting.WithAdditionalArgs([]string{"--config", cfgFilePath})) require.NoError(t, err) ctx, cancel := testcontext.WithDeadline(t, context.Background(), time.Now().Add(10*time.Minute)) @@ -257,7 +257,7 @@ func TestOtelAPMIngestion(t *testing.T) { var fixtureWg sync.WaitGroup fixtureWg.Add(1) go func() { - fixture.RunWithClient(ctx, false, false) + fixture.RunOtelWithClient(ctx, false, false) fixtureWg.Done() }()