-
Notifications
You must be signed in to change notification settings - Fork 254
Support YAML only configuration #2029
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0ef809c
deb9839
0f6378b
d2d63d5
801bab2
55d8527
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| invalid: yaml: content: [ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ import ( | |
| const ( | ||
| exitErrorMessage = "configuration validation first phase failed. Agent version: %v. Verify the JSON input is only using features supported by this version" | ||
| exitSuccessMessage = "Configuration validation first phase succeeded" | ||
| exitSkipMessage = "Configuration validation first phase skipped. No JSON files found" | ||
| version = "1.0" | ||
| envConfigFileName = "env-config.json" | ||
| yamlConfigFileName = "amazon-cloudwatch-agent.yaml" | ||
|
|
@@ -98,12 +99,23 @@ func (ct *ConfigTranslator) Translate() (err error) { | |
| } | ||
| }() | ||
|
|
||
| tomlConfigPath := cmdutil.GetTomlConfigPath(ct.ctx.OutputTomlFilePath()) | ||
| tomlConfigDir := filepath.Dir(tomlConfigPath) | ||
| yamlConfigPath := filepath.Join(tomlConfigDir, yamlConfigFileName) | ||
|
|
||
| mergedJSONConfigMap, err := cmdutil.GenerateMergedJsonConfigMap(ct.ctx) | ||
| if err != nil { | ||
| onlyYAML := errors.Is(err, cmdutil.ErrOnlyYAML) | ||
| if err != nil && !onlyYAML { | ||
| return fmt.Errorf("failed to generate merged json config: %v", err) | ||
| } | ||
| if onlyYAML { | ||
| log.Println(exitSkipMessage) | ||
| // YAML-only configs still require a generated TOML for agent and logging configuration. | ||
| // TOML translation requires a non-nil map. | ||
| mergedJSONConfigMap = map[string]any{} | ||
| } | ||
|
|
||
| if !ct.ctx.RunInContainer() { | ||
| if !onlyYAML && !ct.ctx.RunInContainer() { | ||
| current, err := user.Current() | ||
| if err == nil && current.Name == "****" { | ||
| runAsUser, err := userutil.DetectRunAsUser(mergedJSONConfigMap) | ||
|
|
@@ -114,24 +126,30 @@ func (ct *ConfigTranslator) Translate() (err error) { | |
| } | ||
| } | ||
|
|
||
| tomlConfigPath := cmdutil.GetTomlConfigPath(ct.ctx.OutputTomlFilePath()) | ||
| tomlConfigDir := filepath.Dir(tomlConfigPath) | ||
| yamlConfigPath := filepath.Join(tomlConfigDir, yamlConfigFileName) | ||
| tomlConfig, err := cmdutil.TranslateJsonMapToTomlConfig(mergedJSONConfigMap) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. correct me if this is wrong, but wouldn't this print a misleading error message about validation failure by calling |
||
| if err != nil { | ||
| return fmt.Errorf("failed to generate TOML configuration validation content: %v", err) | ||
| } | ||
| yamlConfig, err := cmdutil.TranslateJsonMapToYamlConfig(mergedJSONConfigMap) | ||
| if err != nil && !errors.Is(err, pipeline.ErrNoPipelines) { | ||
| return fmt.Errorf("failed to generate YAML configuration validation content: %v", err) | ||
| var yamlConfig any | ||
| if !onlyYAML { | ||
| yamlConfig, err = cmdutil.TranslateJsonMapToYamlConfig(mergedJSONConfigMap) | ||
| if err != nil && !errors.Is(err, pipeline.ErrNoPipelines) { | ||
| return fmt.Errorf("failed to generate YAML configuration validation content: %v", err) | ||
| } | ||
| } | ||
| if err = cmdutil.ConfigToTomlFile(tomlConfig, tomlConfigPath); err != nil { | ||
| return fmt.Errorf("failed to create the configuration TOML validation file: %v", err) | ||
| } | ||
| if err = cmdutil.ConfigToYamlFile(yamlConfig, yamlConfigPath); err != nil { | ||
| return fmt.Errorf("failed to create the configuration YAML validation file: %v", err) | ||
| if yamlConfig != nil { | ||
| if err = cmdutil.ConfigToYamlFile(yamlConfig, yamlConfigPath); err != nil { | ||
| return fmt.Errorf("failed to create the configuration YAML validation file: %v", err) | ||
| } | ||
| } else { | ||
| _ = os.Remove(yamlConfigPath) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this cleanup part new? In the scenario that the agent is already running with a valid yaml, if someone now tries to configure with a bad yaml, would this clean up the valid yaml from /etc?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's new because we need to make sure that the previous YAML gets cleaned up if the agent goes from using a JSON to YAML only. |
||
| } | ||
| if !onlyYAML { | ||
| log.Println(exitSuccessMessage) | ||
| } | ||
| log.Println(exitSuccessMessage) | ||
|
|
||
| envConfigPath := filepath.Join(tomlConfigDir, envConfigFileName) | ||
| cmdutil.TranslateJsonMapToEnvConfigFile(mergedJSONConfigMap, envConfigPath) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| package translator | ||
|
|
||
| import ( | ||
| "os" | ||
| "path/filepath" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
|
|
||
| "github.com/aws/amazon-cloudwatch-agent/translator" | ||
| translatorcontext "github.com/aws/amazon-cloudwatch-agent/translator/context" | ||
| translatorutil "github.com/aws/amazon-cloudwatch-agent/translator/util" | ||
| ) | ||
|
|
||
| func TestTranslate_OnlyYAML(t *testing.T) { | ||
| orig := translatorutil.DetectRegion | ||
| translatorutil.DetectRegion = func(string, map[string]string) (string, string) { | ||
| return "us-east-1", "mock" | ||
| } | ||
| defer func() { translatorutil.DetectRegion = orig }() | ||
|
|
||
| translator.ResetMessages() | ||
| translatorcontext.ResetContext() | ||
|
|
||
| tmpDir := t.TempDir() | ||
| require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "config.yaml"), nil, 0600)) | ||
|
|
||
| tomlPath := filepath.Join(tmpDir, "output.toml") | ||
| // Pre-create the YAML output file to verify it gets removed. | ||
| yamlPath := filepath.Join(tmpDir, yamlConfigFileName) | ||
| require.NoError(t, os.WriteFile(yamlPath, nil, 0600)) | ||
|
|
||
| ct, err := NewConfigTranslator("linux", "", tmpDir, tomlPath, "ec2", "", "default") | ||
| require.NoError(t, err) | ||
|
|
||
| assert.NoError(t, ct.Translate()) | ||
| assert.FileExists(t, tomlPath) | ||
| assert.NoFileExists(t, yamlPath) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| package cmdutil | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "log" | ||
| "os" | ||
|
|
@@ -36,6 +37,8 @@ const ( | |
| defaultTomlConfigName = "CWAgent.conf" | ||
| ) | ||
|
|
||
| var ErrOnlyYAML = errors.New("only YAML files detected") | ||
|
|
||
| // TranslateJsonMapToEnvConfigFile populates env-config.json based on the input json config. | ||
| func TranslateJsonMapToEnvConfigFile(jsonConfigValue map[string]interface{}, envConfigPath string) { | ||
| if envConfigPath == "" { | ||
|
|
@@ -115,6 +118,7 @@ func GenerateMergedJsonConfigMap(ctx *context.Context) (map[string]interface{}, | |
| // for the append operation when the existing file name and new .tmp file name have diff | ||
| // only for the ".tmp" suffix, i.e. it is override operation even it says append. | ||
| var jsonConfigMapMap = make(map[string]map[string]interface{}) | ||
| var foundYAML bool | ||
|
|
||
| if ctx.MultiConfig() == "append" || ctx.MultiConfig() == "remove" { | ||
| // backwards compatible for the old json config file | ||
|
|
@@ -163,6 +167,7 @@ func GenerateMergedJsonConfigMap(ctx *context.Context) (map[string]interface{}, | |
| ext = filepath.Ext(key) | ||
| // skip .yaml files | ||
| if ext == constants.FileSuffixYAML { | ||
| foundYAML = true | ||
| return nil | ||
| } | ||
| if ctx.MultiConfig() == "default" || ctx.MultiConfig() == "append" { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So even with this PR, append mode wont work with yamls right?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will. The integration test linked above tests:
I'll create a PR for that integration test in a bit.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
@@ -176,6 +181,7 @@ func GenerateMergedJsonConfigMap(ctx *context.Context) (map[string]interface{}, | |
| } | ||
| } else if ext == constants.FileSuffixYAML { | ||
| // skip .yaml files | ||
| foundYAML = true | ||
| return nil | ||
| } else { | ||
| // non .tmp / existing files | ||
|
|
@@ -208,6 +214,9 @@ func GenerateMergedJsonConfigMap(ctx *context.Context) (map[string]interface{}, | |
| } | ||
| jsonConfigMapMap[config.CWConfigContent] = jm | ||
| } | ||
| if foundYAML && len(jsonConfigMapMap) == 0 { | ||
| return nil, ErrOnlyYAML | ||
| } | ||
| } | ||
|
|
||
| defaultConfig, err := translatorUtil.GetDefaultJsonConfigMap(ctx.Os(), ctx.Mode()) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.