Skip to content

Commit f5edb3f

Browse files
jefchienmusa-asad
andauthored
Add support for supplemental configuration (#1371)
Co-authored-by: Musa <[email protected]>
1 parent 3b0b34e commit f5edb3f

30 files changed

+1005
-175
lines changed

cfg/envconfig/envconfig.go

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
PodName = "POD_NAME"
3131
HostIP = "HOST_IP"
3232
CWConfigContent = "CW_CONFIG_CONTENT"
33+
CWAgentMergedOtelConfig = "CWAGENT_MERGED_OTEL_CONFIG"
3334
)
3435

3536
const (

cmd/amazon-cloudwatch-agent/amazon-cloudwatch-agent.go

+54-14
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import (
3636
"github.com/aws/amazon-cloudwatch-agent/cfg/envconfig"
3737
"github.com/aws/amazon-cloudwatch-agent/cmd/amazon-cloudwatch-agent/internal"
3838
"github.com/aws/amazon-cloudwatch-agent/extension/agenthealth/handler/useragent"
39+
"github.com/aws/amazon-cloudwatch-agent/internal/mapstructure"
40+
"github.com/aws/amazon-cloudwatch-agent/internal/merge/confmap"
3941
"github.com/aws/amazon-cloudwatch-agent/internal/version"
4042
cwaLogger "github.com/aws/amazon-cloudwatch-agent/logger"
4143
"github.com/aws/amazon-cloudwatch-agent/logs"
@@ -46,6 +48,7 @@ import (
4648
"github.com/aws/amazon-cloudwatch-agent/service/defaultcomponents"
4749
"github.com/aws/amazon-cloudwatch-agent/service/registry"
4850
"github.com/aws/amazon-cloudwatch-agent/tool/paths"
51+
"github.com/aws/amazon-cloudwatch-agent/translator/tocwconfig/toyamlconfig"
4952
)
5053

5154
const (
@@ -62,7 +65,7 @@ var fTest = flag.Bool("test", false, "enable test mode: gather metrics, print th
6265
var fTestWait = flag.Int("test-wait", 0, "wait up to this many seconds for service inputs to complete in test mode")
6366
var fSchemaTest = flag.Bool("schematest", false, "validate the toml file schema")
6467
var fTomlConfig = flag.String("config", "", "configuration file to load")
65-
var fOtelConfig = flag.String("otelconfig", paths.YamlConfigPath, "YAML configuration file to run OTel pipeline")
68+
var fOtelConfigs configprovider.OtelConfigFlags
6669
var fEnvConfig = flag.String("envconfig", "", "env configuration file to load")
6770
var fConfigDirectory = flag.String("config-directory", "",
6871
"directory containing additional *.conf files")
@@ -184,7 +187,7 @@ func reloadLoop(
184187
_ = f.Close()
185188
}
186189
}
187-
log.Fatalf("E! [telegraf] Error running agent: %v", err)
190+
log.Fatalf("E! Error running agent: %v", err)
188191
}
189192
}
190193
}
@@ -312,35 +315,65 @@ func runAgent(ctx context.Context,
312315
// Always run logAgent as goroutine regardless of whether starting OTEL or Telegraf.
313316
go logAgent.Run(ctx)
314317

315-
// If OTEL config does not exist, then ASSUME just monitoring logs.
318+
// If only the default CWAgent config yaml is provided and does not exist, then ASSUME
319+
// just monitoring logs since this is the default when no OTEL config flag is provided.
316320
// So just start Telegraf.
317-
_, err = os.Stat(*fOtelConfig)
318-
if errors.Is(err, os.ErrNotExist) {
319-
useragent.Get().SetComponents(&otelcol.Config{}, c)
320-
return ag.Run(ctx)
321+
if len(fOtelConfigs) == 1 && fOtelConfigs[0] == paths.YamlConfigPath {
322+
_, err = os.Stat(fOtelConfigs[0])
323+
if errors.Is(err, os.ErrNotExist) {
324+
useragent.Get().SetComponents(&otelcol.Config{}, c)
325+
return ag.Run(ctx)
326+
}
321327
}
322328
}
323329
// Else start OTEL and rely on adapter package to start the logfile plugin.
324-
yamlConfigPath := *fOtelConfig
325330
level := cwaLogger.ConvertToAtomicLevel(wlog.LogLevel())
326331
logger, loggerOptions := cwaLogger.NewLogger(writer, level)
327-
providerSettings := configprovider.GetSettings(yamlConfigPath, logger)
332+
333+
uris := fOtelConfigs
334+
// merge configs together
335+
if len(uris) > 1 {
336+
log.Printf("D! Merging multiple OTEL configurations: %s", uris)
337+
result := confmap.New()
338+
for _, path := range fOtelConfigs {
339+
conf, err := confmap.LoadConf(path)
340+
if err != nil {
341+
return fmt.Errorf("failed to load OTEL configs: %w", err)
342+
}
343+
if err = result.Merge(conf); err != nil {
344+
return fmt.Errorf("failed to merge OTEL configs: %w", err)
345+
}
346+
}
347+
_ = os.Setenv(envconfig.CWAgentMergedOtelConfig, toyamlconfig.ToYamlConfig(result.ToStringMap()))
348+
uris = []string{"env:" + envconfig.CWAgentMergedOtelConfig}
349+
} else {
350+
_ = os.Unsetenv(envconfig.CWAgentMergedOtelConfig)
351+
}
352+
353+
providerSettings := configprovider.GetSettings(uris, logger)
328354
provider, err := otelcol.NewConfigProvider(providerSettings)
329355
if err != nil {
330-
log.Printf("E! Error while initializing config provider: %v\n", err)
331-
return err
356+
return fmt.Errorf("error while initializing config provider: %v", err)
332357
}
333358

334359
factories, err := components(c)
335360
if err != nil {
336-
log.Printf("E! Error while adapting telegraf input plugins: %v\n", err)
337-
return err
361+
return fmt.Errorf("error while adapting telegraf input plugins: %v", err)
338362
}
339363

340364
cfg, err := provider.Get(ctx, factories)
341365
if err != nil {
342366
return err
343367
}
368+
369+
if _, ok := os.LookupEnv(envconfig.CWAgentMergedOtelConfig); ok {
370+
result, err := mapstructure.Marshal(cfg)
371+
if err != nil {
372+
return fmt.Errorf("failed to marshal OTEL configuration: %v", err)
373+
}
374+
log.Printf("I! Merged OTEL configuration: \n%s\n", toyamlconfig.ToYamlConfig(result))
375+
}
376+
344377
useragent.Get().SetComponents(cfg, c)
345378

346379
params := getCollectorParams(factories, providerSettings, loggerOptions)
@@ -352,7 +385,10 @@ func runAgent(ctx context.Context,
352385
// The config path below here is actually used that was set in the settings above.
353386
// docs: https://github.com/open-telemetry/opentelemetry-collector/blob/93cbae436ae61b832279dbbb18a0d99214b7d305/otelcol/command.go#L63
354387
// *************************************************************************************************
355-
e := []string{"--config=" + yamlConfigPath}
388+
var e []string
389+
for _, uri := range uris {
390+
e = append(e, "--config="+uri)
391+
}
356392
cmd.SetArgs(e)
357393
return cmd.Execute()
358394
}
@@ -422,7 +458,11 @@ func (p *program) Stop(_ service.Service) error {
422458
}
423459

424460
func main() {
461+
flag.Var(&fOtelConfigs, configprovider.OtelConfigFlagName, "YAML configuration files to run OTel pipeline")
425462
flag.Parse()
463+
if len(fOtelConfigs) == 0 {
464+
_ = fOtelConfigs.Set(paths.YamlConfigPath)
465+
}
426466
args := flag.Args()
427467
sectionFilters, inputFilters, outputFilters := []string{}, []string{}, []string{}
428468
if *fSectionFilters != "" {

cmd/config-downloader/downloader.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import (
1818

1919
configaws "github.com/aws/amazon-cloudwatch-agent/cfg/aws"
2020
"github.com/aws/amazon-cloudwatch-agent/cfg/commonconfig"
21+
"github.com/aws/amazon-cloudwatch-agent/internal/constants"
2122
"github.com/aws/amazon-cloudwatch-agent/translator/config"
22-
"github.com/aws/amazon-cloudwatch-agent/translator/context"
2323
"github.com/aws/amazon-cloudwatch-agent/translator/util"
2424
sdkutil "github.com/aws/amazon-cloudwatch-agent/translator/util"
2525
)
@@ -173,7 +173,7 @@ func main() {
173173
return filepath.SkipDir
174174
}
175175
}
176-
if filepath.Ext(path) == context.TmpFileSuffix {
176+
if filepath.Ext(path) == constants.FileSuffixTmp {
177177
return os.Remove(path)
178178
}
179179
return nil
@@ -211,7 +211,7 @@ func main() {
211211
}
212212

213213
if multiConfig != "remove" {
214-
outputFilePath = filepath.Join(outputDir, outputFilePath+context.TmpFileSuffix)
214+
outputFilePath = filepath.Join(outputDir, outputFilePath+constants.FileSuffixTmp)
215215
err = os.WriteFile(outputFilePath, []byte(config), 0644)
216216
if err != nil {
217217
log.Panicf("E! Failed to write the json file %v: %v", outputFilePath, err)

cmd/start-amazon-cloudwatch-agent/path.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/BurntSushi/toml"
1818

1919
"github.com/aws/amazon-cloudwatch-agent/cfg/envconfig"
20+
"github.com/aws/amazon-cloudwatch-agent/internal/util/config"
2021
"github.com/aws/amazon-cloudwatch-agent/internal/util/user"
2122
"github.com/aws/amazon-cloudwatch-agent/tool/paths"
2223
)
@@ -28,9 +29,9 @@ func startAgent(writer io.WriteCloser) error {
2829
paths.AgentBinaryPath, // when using syscall.Exec, must pass binary name as args[0]
2930
"-config", paths.TomlConfigPath,
3031
"-envconfig", paths.EnvConfigPath,
31-
"-otelconfig", paths.YamlConfigPath,
32-
"-pidfile", paths.AgentDir + "/var/amazon-cloudwatch-agent.pid",
3332
}
33+
execArgs = append(execArgs, config.GetOTELConfigArgs(paths.CONFIG_DIR_IN_CONTAINER)...)
34+
execArgs = append(execArgs, "-pidfile", paths.AgentDir+"/var/amazon-cloudwatch-agent.pid")
3435
if err := syscall.Exec(paths.AgentBinaryPath, execArgs, os.Environ()); err != nil {
3536
return fmt.Errorf("error exec as agent binary: %w", err)
3637
}
@@ -69,9 +70,9 @@ func startAgent(writer io.WriteCloser) error {
6970
paths.AgentBinaryPath,
7071
"-config", paths.TomlConfigPath,
7172
"-envconfig", paths.EnvConfigPath,
72-
"-otelconfig", paths.YamlConfigPath,
73-
"-pidfile", paths.AgentDir + "/var/amazon-cloudwatch-agent.pid",
7473
}
74+
agentCmd = append(agentCmd, config.GetOTELConfigArgs(paths.ConfigDirPath)...)
75+
agentCmd = append(agentCmd, "-pidfile", paths.AgentDir+"/var/amazon-cloudwatch-agent.pid")
7576
if err = syscall.Exec(name, agentCmd, os.Environ()); err != nil {
7677
// log file is closed, so use fmt here
7778
fmt.Printf("E! Exec failed: %v \n", err)

cmd/start-amazon-cloudwatch-agent/path_windows.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"os/exec"
1515

1616
"github.com/aws/amazon-cloudwatch-agent/cfg/envconfig"
17+
"github.com/aws/amazon-cloudwatch-agent/internal/util/config"
1718
"github.com/aws/amazon-cloudwatch-agent/tool/paths"
1819
)
1920

@@ -23,24 +24,24 @@ func startAgent(writer io.WriteCloser) error {
2324
log.Printf("E! Cannot close the log file, ERROR is %v \n", err)
2425
return err
2526
}
26-
cmd := exec.Command(
27-
paths.AgentBinaryPath,
27+
execArgs := []string{
2828
"-config", paths.TomlConfigPath,
2929
"-envconfig", paths.EnvConfigPath,
30-
"-otelconfig", paths.YamlConfigPath,
31-
)
30+
}
31+
execArgs = append(execArgs, config.GetOTELConfigArgs(paths.ConfigDirPath)...)
32+
cmd := exec.Command(paths.AgentBinaryPath, execArgs...)
3233
stdoutStderr, err := cmd.CombinedOutput()
3334
// log file is closed, so use fmt here
3435
fmt.Printf("%s \n", stdoutStderr)
3536
return err
3637
} else {
37-
cmd := exec.Command(
38-
paths.AgentBinaryPath,
38+
execArgs := []string{
3939
"-config", paths.TomlConfigPath,
4040
"-envconfig", paths.EnvConfigPath,
41-
"-otelconfig", paths.YamlConfigPath,
42-
"-console", "true",
43-
)
41+
}
42+
execArgs = append(execArgs, config.GetOTELConfigArgs(paths.CONFIG_DIR_IN_CONTAINER)...)
43+
execArgs = append(execArgs, "-console", "true")
44+
cmd := exec.Command(paths.AgentBinaryPath, execArgs...)
4445
cmd.Stdin = os.Stdin
4546
cmd.Stdout = os.Stdout
4647
cmd.Stderr = os.Stderr

cmd/start-amazon-cloudwatch-agent/start-amazon-cloudwatch-agent.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func translateConfig() error {
2424
if envconfig.IsRunningInContainer() {
2525
args = append(args, "--input-dir", paths.CONFIG_DIR_IN_CONTAINER)
2626
} else {
27-
args = append(args, "--input", paths.JsonConfigPath, "--input-dir", paths.JsonDirPath, "--config", paths.CommonConfigPath)
27+
args = append(args, "--input", paths.JsonConfigPath, "--input-dir", paths.ConfigDirPath, "--config", paths.CommonConfigPath)
2828
}
2929
cmd := exec.Command(paths.TranslatorBinaryPath, args...)
3030
cmd.Stdout = os.Stdout

0 commit comments

Comments
 (0)