From 21104dcd9d7417907309d066023e32e0c52010f4 Mon Sep 17 00:00:00 2001 From: Akansha Agarwal Date: Fri, 24 Oct 2025 23:44:25 +0000 Subject: [PATCH 1/6] Introduce windows features adoption metrics Dont use reflect Adds unit tests --- .../handler/useragent/useragent.go | 14 +++++++++ .../handler/useragent/useragent_notwindows.go | 16 ++++++++++ .../handler/useragent/useragent_test.go | 14 +++++++++ .../handler/useragent/useragent_windows.go | 31 +++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 extension/agenthealth/handler/useragent/useragent_notwindows.go create mode 100644 extension/agenthealth/handler/useragent/useragent_windows.go diff --git a/extension/agenthealth/handler/useragent/useragent.go b/extension/agenthealth/handler/useragent/useragent.go index b8c78463bc..1fcaadb46d 100644 --- a/extension/agenthealth/handler/useragent/useragent.go +++ b/extension/agenthealth/handler/useragent/useragent.go @@ -33,12 +33,17 @@ const ( flagEnhancedContainerInsights = "enhanced_container_insights" flagSELinux = "selinux" flagROSA = "rosa" + FlagWindowsEventIDs = "win_event_ids" + FlagWindowsEventFilters = "win_event_filters" + FlagWindowsEventLevels = "win_event_levels" separator = " " typeInputs = "inputs" typeProcessors = "processors" typeOutputs = "outputs" typeFeature = "feature" + + pluginWindowsEventLog = "windows_event_log" ) var ( @@ -79,9 +84,18 @@ var _ UserAgent = (*userAgent)(nil) func (ua *userAgent) SetComponents(otelCfg *otelcol.Config, telegrafCfg *telegraf.Config) { ua.dataLock.Lock() defer ua.dataLock.Unlock() + + winFeatures := collections.NewSet[string]() + for _, input := range telegrafCfg.Inputs { ua.inputs.Add(input.Config.Name) + ua.detectWindowsEventLogFeatures(input, winFeatures) } + + if len(winFeatures) > 0 { + ua.AddFeatureFlags(maps.Keys(winFeatures)...) + } + for _, output := range telegrafCfg.Outputs { ua.outputs.Add(output.Config.Name) } diff --git a/extension/agenthealth/handler/useragent/useragent_notwindows.go b/extension/agenthealth/handler/useragent/useragent_notwindows.go new file mode 100644 index 0000000000..855bb51c35 --- /dev/null +++ b/extension/agenthealth/handler/useragent/useragent_notwindows.go @@ -0,0 +1,16 @@ +//go:build !windows + +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package useragent + +import ( + "github.com/influxdata/telegraf/models" + + "github.com/aws/amazon-cloudwatch-agent/internal/util/collections" +) + +func (ua *userAgent) detectWindowsEventLogFeatures(input *models.RunningInput, winFeatures collections.Set[string]) { + // No-op on non-Windows platforms +} diff --git a/extension/agenthealth/handler/useragent/useragent_test.go b/extension/agenthealth/handler/useragent/useragent_test.go index 983e068e38..c2147edc9b 100644 --- a/extension/agenthealth/handler/useragent/useragent_test.go +++ b/extension/agenthealth/handler/useragent/useragent_test.go @@ -284,3 +284,17 @@ func TestListen(t *testing.T) { ua.SetContainerInsightsFlag() wg.Wait() } + +func TestWindowsEventLogAdoptionMetrics(t *testing.T) { + ua := newUserAgent() + + ua.AddFeatureFlags(FlagWindowsEventIDs, FlagWindowsEventFilters, FlagWindowsEventLevels) + + assert.Len(t, ua.feature, 3) + assert.Equal(t, "feature:(win_event_filters win_event_ids win_event_levels)", ua.featureStr.Load()) + + header := ua.Header(true) + assert.Contains(t, header, FlagWindowsEventIDs) + assert.Contains(t, header, FlagWindowsEventFilters) + assert.Contains(t, header, FlagWindowsEventLevels) +} diff --git a/extension/agenthealth/handler/useragent/useragent_windows.go b/extension/agenthealth/handler/useragent/useragent_windows.go new file mode 100644 index 0000000000..26588eba53 --- /dev/null +++ b/extension/agenthealth/handler/useragent/useragent_windows.go @@ -0,0 +1,31 @@ +//go:build windows + +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package useragent + +import ( + "github.com/influxdata/telegraf/models" + + "github.com/aws/amazon-cloudwatch-agent/internal/util/collections" + "github.com/aws/amazon-cloudwatch-agent/plugins/inputs/windows_event_log" +) + +func (ua *userAgent) detectWindowsEventLogFeatures(input *models.RunningInput, winFeatures collections.Set[string]) { + if input.Config.Name == pluginWindowsEventLog { + if plugin, ok := input.Input.(*windows_event_log.Plugin); ok { + for _, eventConfig := range plugin.Events { + if len(eventConfig.EventIDs) > 0 { + winFeatures.Add(FlagWindowsEventIDs) + } + if len(eventConfig.Filters) > 0 { + winFeatures.Add(FlagWindowsEventFilters) + } + if len(eventConfig.Levels) > 0 { + winFeatures.Add(FlagWindowsEventLevels) + } + } + } + } +} From 702de8a198bcbce491c7b7bb649f83b1e780d402 Mon Sep 17 00:00:00 2001 From: Akansha Agarwal Date: Fri, 24 Oct 2025 23:46:00 +0000 Subject: [PATCH 2/6] lint --- extension/agenthealth/handler/useragent/useragent.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/agenthealth/handler/useragent/useragent.go b/extension/agenthealth/handler/useragent/useragent.go index 1fcaadb46d..ff35fc0a22 100644 --- a/extension/agenthealth/handler/useragent/useragent.go +++ b/extension/agenthealth/handler/useragent/useragent.go @@ -42,7 +42,7 @@ const ( typeProcessors = "processors" typeOutputs = "outputs" typeFeature = "feature" - + pluginWindowsEventLog = "windows_event_log" ) From 83c8ff069e16a28fc1669d36172d255bcffb5f89 Mon Sep 17 00:00:00 2001 From: Akansha Agarwal Date: Mon, 27 Oct 2025 13:37:23 +0000 Subject: [PATCH 3/6] Detect features in windows_event_log --- .../windows_event_log/windows_event_log.go | 17 +++ .../windows_event_log_test.go | 130 +++++------------- 2 files changed, 55 insertions(+), 92 deletions(-) diff --git a/plugins/inputs/windows_event_log/windows_event_log.go b/plugins/inputs/windows_event_log/windows_event_log.go index 69daceaad2..e73e725e2b 100644 --- a/plugins/inputs/windows_event_log/windows_event_log.go +++ b/plugins/inputs/windows_event_log/windows_event_log.go @@ -15,6 +15,7 @@ import ( "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/plugins/inputs" + "github.com/aws/amazon-cloudwatch-agent/extension/agenthealth/handler/useragent" "github.com/aws/amazon-cloudwatch-agent/internal/logscommon" "github.com/aws/amazon-cloudwatch-agent/internal/state" "github.com/aws/amazon-cloudwatch-agent/logs" @@ -92,6 +93,7 @@ func (s *Plugin) Start(acc telegraf.Accumulator) error { return nil } + s.detectFeatures() monitor := newServiceMonitor() for _, eventConfig := range s.Events { // Assume no 2 EventConfigs have the same combination of: @@ -156,3 +158,18 @@ func (s *Plugin) Stop() { func init() { inputs.Add("windows_event_log", func() telegraf.Input { return &Plugin{} }) } +func (s *Plugin) detectFeatures() { + if ua := useragent.Get(); ua != nil { + for _, eventConfig := range s.Events { + if len(eventConfig.EventIDs) > 0 { + ua.AddFeatureFlags(useragent.FlagWindowsEventIDs) + } + if len(eventConfig.Filters) > 0 { + ua.AddFeatureFlags(useragent.FlagWindowsEventFilters) + } + if len(eventConfig.Levels) > 0 { + ua.AddFeatureFlags(useragent.FlagWindowsEventLevels) + } + } + } +} diff --git a/plugins/inputs/windows_event_log/windows_event_log_test.go b/plugins/inputs/windows_event_log/windows_event_log_test.go index 3b0d530052..50253e359c 100644 --- a/plugins/inputs/windows_event_log/windows_event_log_test.go +++ b/plugins/inputs/windows_event_log/windows_event_log_test.go @@ -7,101 +7,47 @@ package windows_event_log import ( - "os" - "path/filepath" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// TestGetStateFilePathGood tests getStateFilePath with good input. -func TestGetStateFilePathGood(t *testing.T) { - fileStateFolder := t.TempDir() - plugin := Plugin{ - FileStateFolder: fileStateFolder, - } - ec := EventConfig{ - LogGroupName: "MyGroup", - LogStreamName: "MyStream", - Name: "SystemEventLog", - } - stateManagerCfg, err := getStateManagerConfig(&plugin, &ec) - assert.NoError(t, err) - t.Log(stateManagerCfg.StateFilePath()) - expected := filepath.Join(fileStateFolder, - "Amazon_CloudWatch_WindowsEventLog_MyGroup_MyStream_SystemEventLog") - assert.Equal(t, expected, stateManagerCfg.StateFilePath()) - _, err = os.Stat(fileStateFolder) - assert.False(t, os.IsNotExist(err)) -} -// TestGetStateFilePathEscape tests getStateFilePath() with special characters. -func TestGetStateFilePathEscape(t *testing.T) { - fileStateFolder := t.TempDir() - plugin := Plugin{ - FileStateFolder: fileStateFolder, - } - ec := EventConfig{ - LogGroupName: "My Group/:::", - LogStreamName: "My::Stream// ", - Name: "System Event//Log::", - } - stateManagerCfg, err := getStateManagerConfig(&plugin, &ec) - assert.NoError(t, err) - t.Log(stateManagerCfg.StateFilePath()) - expected := filepath.Join(fileStateFolder, - "Amazon_CloudWatch_WindowsEventLog_My__Group_____My__Stream_____System__Event__Log__") - assert.Equal(t, expected, stateManagerCfg.StateFilePath()) -} - -// TestGetStateFilePathEmpty tests getStateFilePath() with empty folder. -func TestGetStateFilePathEmpty(t *testing.T) { - fileStateFolder := "" - plugin := Plugin{ - FileStateFolder: fileStateFolder, - } - ec := EventConfig{ - LogGroupName: "MyGroup", - LogStreamName: "MyStream", - Name: "SystemEventLog", - } - stateManagerCfg, err := getStateManagerConfig(&plugin, &ec) - t.Log(stateManagerCfg.StateFilePath()) - assert.Error(t, err) -} - -// TestGetStateFilePathSpecialChars tests getStateFilePath() with bad folder. -func TestGetStateFilePathSpecialChars(t *testing.T) { - fileStateFolder := "F:\\\\bin!@#$%^&*)(\\CloudWatchAgentTest" - // cleanup - plugin := Plugin{ - FileStateFolder: fileStateFolder, - } - ec := EventConfig{ - LogGroupName: "MyGroup", - LogStreamName: "MyStream", - Name: "SystemEventLog", - } - stateManagerCfg, err := getStateManagerConfig(&plugin, &ec) - t.Log(stateManagerCfg.StateFilePath()) - assert.Error(t, err) -} + "github.com/aws/amazon-cloudwatch-agent/extension/agenthealth/handler/useragent" + "github.com/aws/amazon-cloudwatch-agent/plugins/inputs/windows_event_log/wineventlog" +) -func TestWindowsDuplicateStart(t *testing.T) { - fileStateFolder := filepath.Join(t.TempDir(), "CloudWatchAgentTest") - plugin := Plugin{ - FileStateFolder: fileStateFolder, - } - ec := EventConfig{ - LogGroupName: "My Group/:::", - LogStreamName: "My::Stream// ", - Name: "System Event//Log::", - } - plugin.Events = append(plugin.Events, ec) - require.Equal(t, 0, len(plugin.newEvents), "Start should be ran only once so there should be only 1 new event") - plugin.Start(nil) - require.Equal(t, 1, len(plugin.newEvents), "Start should be ran only once so there should be only 1 new event") - plugin.Start(nil) - require.Equal(t, 1, len(plugin.newEvents), "Start should be ran only once so there should be only 1 new event") +func TestDetectFeatures(t *testing.T) { + plugin := &Plugin{ + Events: []EventConfig{ + { + EventIDs: []int{1000, 1001}, + }, + { + Filters: []*wineventlog.EventFilter{{Expression: "test"}}, + Levels: []string{"ERROR"}, + }, + }, + } + + ua := useragent.Get() + plugin.detectFeatures() + + header := ua.Header(true) + assert.Contains(t, header, useragent.FlagWindowsEventIDs) + assert.Contains(t, header, useragent.FlagWindowsEventFilters) + assert.Contains(t, header, useragent.FlagWindowsEventLevels) + + // Test that only configured features are detected + plugin = &Plugin{ + Events: []EventConfig{{ + EventIDs: []int{1000}, + }}, + } + ua = useragent.Get() + ua.Reset() + plugin.detectFeatures() + + header = ua.Header(true) + assert.Contains(t, header, useragent.FlagWindowsEventIDs) + assert.NotContains(t, header, useragent.FlagWindowsEventFilters) + assert.NotContains(t, header, useragent.FlagWindowsEventLevels) } From 683f5f58fccb02ea8f697d5507c9c06d3dcb6fed Mon Sep 17 00:00:00 2001 From: Akansha Agarwal Date: Mon, 27 Oct 2025 13:46:07 +0000 Subject: [PATCH 4/6] remove unused files --- .../handler/useragent/useragent_notwindows.go | 16 ---------- .../handler/useragent/useragent_windows.go | 31 ------------------- 2 files changed, 47 deletions(-) delete mode 100644 extension/agenthealth/handler/useragent/useragent_notwindows.go delete mode 100644 extension/agenthealth/handler/useragent/useragent_windows.go diff --git a/extension/agenthealth/handler/useragent/useragent_notwindows.go b/extension/agenthealth/handler/useragent/useragent_notwindows.go deleted file mode 100644 index 855bb51c35..0000000000 --- a/extension/agenthealth/handler/useragent/useragent_notwindows.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build !windows - -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package useragent - -import ( - "github.com/influxdata/telegraf/models" - - "github.com/aws/amazon-cloudwatch-agent/internal/util/collections" -) - -func (ua *userAgent) detectWindowsEventLogFeatures(input *models.RunningInput, winFeatures collections.Set[string]) { - // No-op on non-Windows platforms -} diff --git a/extension/agenthealth/handler/useragent/useragent_windows.go b/extension/agenthealth/handler/useragent/useragent_windows.go deleted file mode 100644 index 26588eba53..0000000000 --- a/extension/agenthealth/handler/useragent/useragent_windows.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build windows - -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package useragent - -import ( - "github.com/influxdata/telegraf/models" - - "github.com/aws/amazon-cloudwatch-agent/internal/util/collections" - "github.com/aws/amazon-cloudwatch-agent/plugins/inputs/windows_event_log" -) - -func (ua *userAgent) detectWindowsEventLogFeatures(input *models.RunningInput, winFeatures collections.Set[string]) { - if input.Config.Name == pluginWindowsEventLog { - if plugin, ok := input.Input.(*windows_event_log.Plugin); ok { - for _, eventConfig := range plugin.Events { - if len(eventConfig.EventIDs) > 0 { - winFeatures.Add(FlagWindowsEventIDs) - } - if len(eventConfig.Filters) > 0 { - winFeatures.Add(FlagWindowsEventFilters) - } - if len(eventConfig.Levels) > 0 { - winFeatures.Add(FlagWindowsEventLevels) - } - } - } - } -} From 835d4897ddb5f21775747c0d786a1c1beb16158e Mon Sep 17 00:00:00 2001 From: Akansha Agarwal Date: Mon, 27 Oct 2025 13:46:38 +0000 Subject: [PATCH 5/6] remove unused files --- .../agenthealth/handler/useragent/useragent.go | 9 --------- .../handler/useragent/useragent_test.go | 14 -------------- 2 files changed, 23 deletions(-) diff --git a/extension/agenthealth/handler/useragent/useragent.go b/extension/agenthealth/handler/useragent/useragent.go index ff35fc0a22..b6d9ce88e4 100644 --- a/extension/agenthealth/handler/useragent/useragent.go +++ b/extension/agenthealth/handler/useragent/useragent.go @@ -42,8 +42,6 @@ const ( typeProcessors = "processors" typeOutputs = "outputs" typeFeature = "feature" - - pluginWindowsEventLog = "windows_event_log" ) var ( @@ -85,15 +83,8 @@ func (ua *userAgent) SetComponents(otelCfg *otelcol.Config, telegrafCfg *telegra ua.dataLock.Lock() defer ua.dataLock.Unlock() - winFeatures := collections.NewSet[string]() - for _, input := range telegrafCfg.Inputs { ua.inputs.Add(input.Config.Name) - ua.detectWindowsEventLogFeatures(input, winFeatures) - } - - if len(winFeatures) > 0 { - ua.AddFeatureFlags(maps.Keys(winFeatures)...) } for _, output := range telegrafCfg.Outputs { diff --git a/extension/agenthealth/handler/useragent/useragent_test.go b/extension/agenthealth/handler/useragent/useragent_test.go index c2147edc9b..983e068e38 100644 --- a/extension/agenthealth/handler/useragent/useragent_test.go +++ b/extension/agenthealth/handler/useragent/useragent_test.go @@ -284,17 +284,3 @@ func TestListen(t *testing.T) { ua.SetContainerInsightsFlag() wg.Wait() } - -func TestWindowsEventLogAdoptionMetrics(t *testing.T) { - ua := newUserAgent() - - ua.AddFeatureFlags(FlagWindowsEventIDs, FlagWindowsEventFilters, FlagWindowsEventLevels) - - assert.Len(t, ua.feature, 3) - assert.Equal(t, "feature:(win_event_filters win_event_ids win_event_levels)", ua.featureStr.Load()) - - header := ua.Header(true) - assert.Contains(t, header, FlagWindowsEventIDs) - assert.Contains(t, header, FlagWindowsEventFilters) - assert.Contains(t, header, FlagWindowsEventLevels) -} From d167b9435f74778e79b50801d03a5b32ab96bd7d Mon Sep 17 00:00:00 2001 From: Akansha Agarwal Date: Mon, 27 Oct 2025 13:49:47 +0000 Subject: [PATCH 6/6] Update unit test --- .../windows_event_log_test.go | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/plugins/inputs/windows_event_log/windows_event_log_test.go b/plugins/inputs/windows_event_log/windows_event_log_test.go index 50253e359c..526e3ebed5 100644 --- a/plugins/inputs/windows_event_log/windows_event_log_test.go +++ b/plugins/inputs/windows_event_log/windows_event_log_test.go @@ -7,14 +7,108 @@ package windows_event_log import ( + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/aws/amazon-cloudwatch-agent/extension/agenthealth/handler/useragent" "github.com/aws/amazon-cloudwatch-agent/plugins/inputs/windows_event_log/wineventlog" ) +// TestGetStateFilePathGood tests getStateFilePath with good input. +func TestGetStateFilePathGood(t *testing.T) { + fileStateFolder := t.TempDir() + plugin := Plugin{ + FileStateFolder: fileStateFolder, + } + ec := EventConfig{ + LogGroupName: "MyGroup", + LogStreamName: "MyStream", + Name: "SystemEventLog", + } + stateManagerCfg, err := getStateManagerConfig(&plugin, &ec) + assert.NoError(t, err) + t.Log(stateManagerCfg.StateFilePath()) + expected := filepath.Join(fileStateFolder, + "Amazon_CloudWatch_WindowsEventLog_MyGroup_MyStream_SystemEventLog") + assert.Equal(t, expected, stateManagerCfg.StateFilePath()) + _, err = os.Stat(fileStateFolder) + assert.False(t, os.IsNotExist(err)) +} + +// TestGetStateFilePathEscape tests getStateFilePath() with special characters. +func TestGetStateFilePathEscape(t *testing.T) { + fileStateFolder := t.TempDir() + plugin := Plugin{ + FileStateFolder: fileStateFolder, + } + ec := EventConfig{ + LogGroupName: "My Group/:::", + LogStreamName: "My::Stream// ", + Name: "System Event//Log::", + } + stateManagerCfg, err := getStateManagerConfig(&plugin, &ec) + assert.NoError(t, err) + t.Log(stateManagerCfg.StateFilePath()) + expected := filepath.Join(fileStateFolder, + "Amazon_CloudWatch_WindowsEventLog_My__Group_____My__Stream_____System__Event__Log__") + assert.Equal(t, expected, stateManagerCfg.StateFilePath()) +} + +// TestGetStateFilePathEmpty tests getStateFilePath() with empty folder. +func TestGetStateFilePathEmpty(t *testing.T) { + fileStateFolder := "" + plugin := Plugin{ + FileStateFolder: fileStateFolder, + } + ec := EventConfig{ + LogGroupName: "MyGroup", + LogStreamName: "MyStream", + Name: "SystemEventLog", + } + stateManagerCfg, err := getStateManagerConfig(&plugin, &ec) + t.Log(stateManagerCfg.StateFilePath()) + assert.Error(t, err) +} + +// TestGetStateFilePathSpecialChars tests getStateFilePath() with bad folder. +func TestGetStateFilePathSpecialChars(t *testing.T) { + fileStateFolder := "F:\\\\bin!@#$%^&*)(\\CloudWatchAgentTest" + // cleanup + plugin := Plugin{ + FileStateFolder: fileStateFolder, + } + ec := EventConfig{ + LogGroupName: "MyGroup", + LogStreamName: "MyStream", + Name: "SystemEventLog", + } + stateManagerCfg, err := getStateManagerConfig(&plugin, &ec) + t.Log(stateManagerCfg.StateFilePath()) + assert.Error(t, err) +} + +func TestWindowsDuplicateStart(t *testing.T) { + fileStateFolder := filepath.Join(t.TempDir(), "CloudWatchAgentTest") + plugin := Plugin{ + FileStateFolder: fileStateFolder, + } + ec := EventConfig{ + LogGroupName: "My Group/:::", + LogStreamName: "My::Stream// ", + Name: "System Event//Log::", + } + plugin.Events = append(plugin.Events, ec) + require.Equal(t, 0, len(plugin.newEvents), "Start should be ran only once so there should be only 1 new event") + plugin.Start(nil) + require.Equal(t, 1, len(plugin.newEvents), "Start should be ran only once so there should be only 1 new event") + plugin.Start(nil) + require.Equal(t, 1, len(plugin.newEvents), "Start should be ran only once so there should be only 1 new event") +} + func TestDetectFeatures(t *testing.T) { plugin := &Plugin{ Events: []EventConfig{