diff --git a/extension/agenthealth/handler/stats/provider/interval_test.go b/extension/agenthealth/handler/stats/provider/interval_test.go index cd2be8899b..d56c23b8ae 100644 --- a/extension/agenthealth/handler/stats/provider/interval_test.go +++ b/extension/agenthealth/handler/stats/provider/interval_test.go @@ -18,13 +18,10 @@ func TestIntervalStats(t *testing.T) { s.stats.Store(agent.Stats{ ThreadCount: aws.Int32(2), }) - got := s.Stats("") - assert.NotNil(t, got.ThreadCount) - got = s.Stats("") - assert.Nil(t, got.ThreadCount) - time.Sleep(2 * time.Millisecond) - got = s.Stats("") - assert.NotNil(t, got.ThreadCount) - got = s.Stats("") - assert.Nil(t, got.ThreadCount) + assert.NotNil(t, s.Stats("").ThreadCount) + assert.Nil(t, s.Stats("").ThreadCount) + time.Sleep(time.Millisecond) + assert.Eventually(t, func() bool { + return s.Stats("").ThreadCount != nil + }, 5*time.Millisecond, time.Millisecond) } diff --git a/extension/agenthealth/handler/stats/provider/process_test.go b/extension/agenthealth/handler/stats/provider/process_test.go index 3448ad2fcd..ea9a56b602 100644 --- a/extension/agenthealth/handler/stats/provider/process_test.go +++ b/extension/agenthealth/handler/stats/provider/process_test.go @@ -5,41 +5,53 @@ package provider import ( "errors" + "sync" "testing" "time" "github.com/shirou/gopsutil/v3/process" "github.com/stretchr/testify/assert" + + "github.com/aws/amazon-cloudwatch-agent/extension/agenthealth/handler/stats/agent" ) type mockProcessMetrics struct { + mu sync.RWMutex err error } var _ processMetrics = (*mockProcessMetrics)(nil) -func (m mockProcessMetrics) CPUPercent() (float64, error) { +func (m *mockProcessMetrics) CPUPercent() (float64, error) { + m.mu.RLock() + defer m.mu.RUnlock() if m.err != nil { return -1, m.err } return 1, nil } -func (m mockProcessMetrics) MemoryInfo() (*process.MemoryInfoStat, error) { +func (m *mockProcessMetrics) MemoryInfo() (*process.MemoryInfoStat, error) { + m.mu.RLock() + defer m.mu.RUnlock() if m.err != nil { return nil, m.err } return &process.MemoryInfoStat{RSS: uint64(2)}, nil } -func (m mockProcessMetrics) NumFDs() (int32, error) { +func (m *mockProcessMetrics) NumFDs() (int32, error) { + m.mu.RLock() + defer m.mu.RUnlock() if m.err != nil { return -1, m.err } return 3, nil } -func (m mockProcessMetrics) NumThreads() (int32, error) { +func (m *mockProcessMetrics) NumThreads() (int32, error) { + m.mu.RLock() + defer m.mu.RUnlock() if m.err != nil { return -1, m.err } @@ -59,11 +71,11 @@ func TestProcessStats(t *testing.T) { assert.EqualValues(t, 2, *got.MemoryBytes) assert.EqualValues(t, 3, *got.FileDescriptorCount) assert.EqualValues(t, 4, *got.ThreadCount) + mock.mu.Lock() mock.err = testErr - time.Sleep(2 * time.Millisecond) - got = provider.getStats() - assert.Nil(t, got.CpuPercent) - assert.Nil(t, got.MemoryBytes) - assert.Nil(t, got.FileDescriptorCount) - assert.Nil(t, got.ThreadCount) + mock.mu.Unlock() + provider.refresh() + assert.Eventually(t, func() bool { + return provider.getStats() == agent.Stats{} + }, 5*time.Millisecond, time.Millisecond) } diff --git a/handlers/agentinfo/info.go b/handlers/agentinfo/info.go deleted file mode 100644 index 5e95aae31b..0000000000 --- a/handlers/agentinfo/info.go +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package agentinfo - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "os" - "path/filepath" - "regexp" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/google/uuid" - "github.com/influxdata/telegraf/config" - "github.com/shirou/gopsutil/v3/process" - "go.opentelemetry.io/collector/otelcol" - "golang.org/x/exp/maps" - - "github.com/aws/amazon-cloudwatch-agent/cfg/envconfig" - "github.com/aws/amazon-cloudwatch-agent/internal/util/collections" - "github.com/aws/amazon-cloudwatch-agent/receiver/adapter" - translatorConfig "github.com/aws/amazon-cloudwatch-agent/translator/config" -) - -const ( - versionFilename = "CWAGENT_VERSION" - unknownVersion = "Unknown" - updateInterval = time.Minute - // region types - AgentConfigJson = "ACJ" - CredsMap = "CM" - EC2Metadata = "EC2M" - ECSMetadata = "ECSM" - RegionNotFound = "RNF" - ModeEC2 = "EC2" - ModeOnPrem = "OP" - ModeWithIRSA = "WI" -) - -var ( - receivers string - processors string - exporters string - usageDataEnabled bool - onceUsageData sync.Once - containerInsightsRegexp = regexp.MustCompile("^/aws/.*containerinsights/.*/(performance|prometheus)$") - version = readVersionFile() - fullVersion = getFullVersion(version) - id = uuid.NewString() - sharedConfigFallback atomic.Bool - imdsFallbackSucceed atomic.Bool - isRunningAsRoot = defaultIsRunningAsRoot - runInContainer *int - regionType *string - mode *string -) - -type AgentInfo interface { - RecordOpData(time.Duration, int, error) - StatsHeader() string - UserAgent() string -} - -type agentInfo struct { - proc *process.Process - nextUpdate time.Time - statsHeader string - userAgent string -} - -type agentStats struct { - CpuPercent *float64 `json:"cpu,omitempty"` - MemoryBytes *uint64 `json:"mem,omitempty"` - FileDescriptorCount *int32 `json:"fd,omitempty"` - ThreadCount *int32 `json:"th,omitempty"` - LatencyMillis *int64 `json:"lat,omitempty"` - PayloadBytes *int `json:"load,omitempty"` - StatusCode *int `json:"code,omitempty"` - SharedConfigFallback *int `json:"scfb,omitempty"` - ImdsFallbackSucceed *int `json:"ifs,omitempty"` - RunInContainer *int `json:"ric,omitempty"` - RegionType *string `json:"rt,omitempty"` - Mode *string `json:"m,omitempty"` -} - -func New(groupName string, regionType string, mode string) AgentInfo { - return newAgentInfo(groupName, regionType, mode) -} - -func newAgentInfo(groupName string, regionTypeInput string, modeInput string) *agentInfo { - ai := new(agentInfo) - ai.userAgent = getUserAgent(groupName, fullVersion, receivers, processors, exporters, isUsageDataEnabled()) - runInContainer = runInContainerFunc() - regionType = aws.String(regionTypeInput) - mode = aws.String(modeInput) - if isUsageDataEnabled() { - ai.proc, _ = process.NewProcess(int32(os.Getpid())) - if ai.proc == nil { - return ai - } - stats := agentStats{ - CpuPercent: ai.cpuPercent(), - MemoryBytes: ai.memoryBytes(), - FileDescriptorCount: ai.fileDescriptorCount(), - ThreadCount: ai.threadCount(), - RunInContainer: runInContainer, - RegionType: regionType, - Mode: mode, - } - ai.statsHeader = getAgentStats(stats) - ai.nextUpdate = time.Now().Add(updateInterval) - } - - return ai -} - -func (ai *agentInfo) UserAgent() string { - return ai.userAgent -} - -func (ai *agentInfo) RecordOpData(latency time.Duration, payloadBytes int, err error) { - if ai.proc == nil { - return - } - - stats := agentStats{ - LatencyMillis: aws.Int64(latency.Milliseconds()), - PayloadBytes: aws.Int(payloadBytes), - StatusCode: getStatusCode(err), - } - - if now := time.Now(); now.After(ai.nextUpdate) { - stats.CpuPercent = ai.cpuPercent() - stats.MemoryBytes = ai.memoryBytes() - stats.FileDescriptorCount = ai.fileDescriptorCount() - stats.ThreadCount = ai.threadCount() - stats.SharedConfigFallback = getSharedConfigFallback() - stats.ImdsFallbackSucceed = succeedImdsFallback() - stats.RunInContainer = runInContainer - stats.RegionType = regionType - stats.Mode = mode - ai.nextUpdate = now.Add(updateInterval) - } - - ai.statsHeader = getAgentStats(stats) -} - -func (ai *agentInfo) StatsHeader() string { - return ai.statsHeader -} - -func (ai *agentInfo) cpuPercent() *float64 { - if cpuPercent, err := ai.proc.CPUPercent(); err == nil { - return aws.Float64(float64(int64(cpuPercent*10)) / 10) // truncate to 10th decimal place - } - return nil -} - -func (ai *agentInfo) memoryBytes() *uint64 { - if memInfo, err := ai.proc.MemoryInfo(); err == nil { - return aws.Uint64(memInfo.RSS) - } - return nil -} - -func (ai *agentInfo) fileDescriptorCount() *int32 { - if fdCount, err := ai.proc.NumFDs(); err == nil { - return aws.Int32(fdCount) - } - return nil -} - -// we only need to know if value is 1 -// thus return nil if not set -func succeedImdsFallback() *int { - if imdsFallbackSucceed.Load() { - return aws.Int(1) - } - return nil -} - -func (ai *agentInfo) threadCount() *int32 { - if thCount, err := ai.proc.NumThreads(); err == nil { - return aws.Int32(thCount) - } - return nil -} - -func SetComponents(otelcfg *otelcol.Config, telegrafcfg *config.Config) { - receiverSet := collections.NewSet[string]() - processorSet := collections.NewSet[string]() - exporterSet := collections.NewSet[string]() - - for _, input := range telegrafcfg.Inputs { - receiverSet.Add(input.Config.Name) - } - for _, output := range telegrafcfg.Outputs { - exporterSet.Add(output.Config.Name) - } - - for _, pipeline := range otelcfg.Service.Pipelines { - for _, receiver := range pipeline.Receivers { - // trim the adapter prefix from adapted Telegraf plugins - name := strings.TrimPrefix(string(receiver.Type()), adapter.TelegrafPrefix) - receiverSet.Add(name) - } - for _, processor := range pipeline.Processors { - processorSet.Add(string(processor.Type())) - } - for _, exporter := range pipeline.Exporters { - exporterSet.Add(string(exporter.Type())) - } - } - - if !isRunningAsRoot() { - receiverSet.Add("run_as_user") - } - - receiversSlice := maps.Keys(receiverSet) - processorsSlice := maps.Keys(processorSet) - exportersSlice := maps.Keys(exporterSet) - - sort.Strings(receiversSlice) - sort.Strings(processorsSlice) - sort.Strings(exportersSlice) - - receivers = strings.Join(receiversSlice, " ") - processors = strings.Join(processorsSlice, " ") - exporters = strings.Join(exportersSlice, " ") -} - -func Version() string { - return version -} - -func FullVersion() string { - return fullVersion -} - -func getAgentStats(stats agentStats) string { - raw, err := json.Marshal(stats) - if err != nil { - log.Printf("W! Failed to serialize agent stats, error: %s", err) - return "" - } - content := strings.TrimPrefix(string(raw), "{") - return strings.TrimSuffix(content, "}") -} - -func getStatusCode(err error) *int { - if err == nil { - return aws.Int(http.StatusOK) - } - if reqErr, ok := err.(awserr.RequestFailure); ok { - return aws.Int(reqErr.StatusCode()) - } - return nil -} - -func getUserAgent(groupName, fullVersion, receivers, processors, exporters string, usageDataEnabled bool) string { - if ua := os.Getenv(envconfig.CWAGENT_USER_AGENT); ua != "" { - return ua - } - if !usageDataEnabled { - return fullVersion - } - - outputs := strings.Clone(exporters) - if outputs != "" && containerInsightsRegexp.MatchString(groupName) && !strings.Contains(outputs, "container_insights") { - outputs += " container_insights" - } - - components := make([]string, 0, 0) - if receivers != "" { - components = append(components, fmt.Sprintf("inputs:(%s)", receivers)) - } - if processors != "" { - components = append(components, fmt.Sprintf("processors:(%s)", processors)) - } - if outputs != "" { - components = append(components, fmt.Sprintf("outputs:(%s)", outputs)) - } - - return strings.TrimSpace(fmt.Sprintf("%s ID/%s %s", fullVersion, id, strings.Join(components, " "))) -} - -func getFullVersion(version string) string { - return fmt.Sprintf("CWAgent/%s (%s; %s; %s)", - version, - runtime.Version(), - runtime.GOOS, - runtime.GOARCH) -} - -func readVersionFile() string { - ex, err := os.Executable() - if err != nil { - return unknownVersion - } - - versionFilePath := filepath.Join(filepath.Dir(ex), versionFilename) - if _, err := os.Stat(versionFilePath); err != nil { - return unknownVersion - } - - byteArray, err := os.ReadFile(versionFilePath) - if err != nil { - return unknownVersion - } - - return strings.Trim(string(byteArray), " \n\r\t") -} - -// this returns true for true or invalid -// examples of invalid are not set env var, "", "invalid" -func getUsageDataEnabled() bool { - ok, err := strconv.ParseBool(os.Getenv(envconfig.CWAGENT_USAGE_DATA)) - return ok || err != nil -} - -func isUsageDataEnabled() bool { - onceUsageData.Do(func() { - usageDataEnabled = getUsageDataEnabled() - }) - return usageDataEnabled -} - -func defaultIsRunningAsRoot() bool { - return os.Getuid() == 0 -} - -func RecordSharedConfigFallback() { - sharedConfigFallback.Store(true) -} - -func getSharedConfigFallback() *int { - if sharedConfigFallback.Load() { - return aws.Int(1) - } - return nil -} - -func SetImdsFallbackSucceed() { - imdsFallbackSucceed.Store(true) -} - -func runInContainerFunc() *int { - if os.Getenv(translatorConfig.RUN_IN_CONTAINER) == translatorConfig.RUN_IN_CONTAINER_TRUE { - return aws.Int(1) - } - return aws.Int(0) -} diff --git a/handlers/agentinfo/info_test.go b/handlers/agentinfo/info_test.go deleted file mode 100644 index edc0b05dd8..0000000000 --- a/handlers/agentinfo/info_test.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT - -package agentinfo - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" - "os" - "path/filepath" - "regexp" - "runtime" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/influxdata/telegraf/config" - "github.com/influxdata/telegraf/models" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/otelcol" - "go.opentelemetry.io/collector/service" - - "github.com/aws/amazon-cloudwatch-agent/cfg/envconfig" - "github.com/aws/amazon-cloudwatch-agent/receiver/adapter" -) - -func TestNew(t *testing.T) { - ai := New("", "", "") - expectedUserAgentRegex := `^CWAgent/Unknown \(.*\) ` + - `ID/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$` - - assert.Regexp(t, regexp.MustCompile(expectedUserAgentRegex), ai.UserAgent()) -} - -func TestRecordOpData(t *testing.T) { - ai := newAgentInfo("", "", "") - - stats := ai.StatsHeader() - actual := agentStats{} - require.NoError(t, json.Unmarshal([]byte("{"+stats+"}"), &actual)) - assert.Nil(t, actual.LatencyMillis) - assert.Nil(t, actual.PayloadBytes) - assert.Nil(t, actual.StatusCode) - - ai.RecordOpData(100000000, 10, nil) - stats = ai.StatsHeader() - actual = agentStats{} - require.NoError(t, json.Unmarshal([]byte("{"+stats+"}"), &actual)) - assert.EqualValues(t, 100, *actual.LatencyMillis) - assert.EqualValues(t, 10, *actual.PayloadBytes) - assert.EqualValues(t, http.StatusOK, *actual.StatusCode) - - ai.RecordOpData(200000000, 20, errors.New("")) - stats = ai.StatsHeader() - actual = agentStats{} - require.NoError(t, json.Unmarshal([]byte("{"+stats+"}"), &actual)) - assert.EqualValues(t, 200, *actual.LatencyMillis) - assert.EqualValues(t, 20, *actual.PayloadBytes) - assert.Nil(t, actual.StatusCode) - - ai.RecordOpData(300000000, 30, awserr.NewRequestFailure(awserr.New("", "", errors.New("")), 500, "")) - stats = ai.StatsHeader() - actual = agentStats{} - require.NoError(t, json.Unmarshal([]byte("{"+stats+"}"), &actual)) - assert.EqualValues(t, 300, *actual.LatencyMillis) - assert.EqualValues(t, 30, *actual.PayloadBytes) - assert.EqualValues(t, 500, *actual.StatusCode) -} - -func TestSetComponents(t *testing.T) { - otelcfg := &otelcol.Config{ - Service: service.Config{ - Pipelines: map[component.ID]*service.PipelineConfig{ - component.NewID("metrics"): { - Receivers: []component.ID{ - component.NewID(adapter.TelegrafPrefix + "cpu"), - component.NewID("prometheus"), - }, - Processors: []component.ID{ - component.NewID("batch"), - component.NewID("filter"), - }, - Exporters: []component.ID{ - component.NewID("cloudwatch"), - }, - }, - }, - }, - } - telegrafcfg := &config.Config{ - Inputs: []*models.RunningInput{ - {Config: &models.InputConfig{Name: "logs"}}, - {Config: &models.InputConfig{Name: "cpu"}}, - }, - Outputs: []*models.RunningOutput{ - {Config: &models.OutputConfig{Name: "cloudwatchlogs"}}, - }, - } - - SetComponents(otelcfg, telegrafcfg) - assert.Equal(t, "cpu logs prometheus run_as_user", receivers) - assert.Equal(t, "batch filter", processors) - assert.Equal(t, "cloudwatch cloudwatchlogs", exporters) - - isRunningAsRoot = func() bool { return true } - SetComponents(otelcfg, telegrafcfg) - assert.Equal(t, "cpu logs prometheus", receivers) - assert.Equal(t, "batch filter", processors) - assert.Equal(t, "cloudwatch cloudwatchlogs", exporters) - isRunningAsRoot = defaultIsRunningAsRoot -} - -func TestGetAgentStats(t *testing.T) { - stats := agentStats{ - CpuPercent: aws.Float64(1.2), - MemoryBytes: aws.Uint64(123), - FileDescriptorCount: aws.Int32(456), - ThreadCount: aws.Int32(789), - LatencyMillis: aws.Int64(1234), - PayloadBytes: aws.Int(5678), - StatusCode: aws.Int(200), - ImdsFallbackSucceed: aws.Int(1), - RunInContainer: aws.Int(0), - RegionType: aws.String(EC2Metadata), - Mode: aws.String(ModeWithIRSA), - } - - assert.Equal(t, "\"cpu\":1.2,\"mem\":123,\"fd\":456,\"th\":789,\"lat\":1234,\"load\":5678,\"code\":200,\"ifs\":1,\"ric\":0,\"rt\":\"EC2M\",\"m\":\"WI\"", getAgentStats(stats)) - - stats.Mode = nil - assert.Equal(t, "\"cpu\":1.2,\"mem\":123,\"fd\":456,\"th\":789,\"lat\":1234,\"load\":5678,\"code\":200,\"ifs\":1,\"ric\":0,\"rt\":\"EC2M\"", getAgentStats(stats)) - - stats.RegionType = nil - assert.Equal(t, "\"cpu\":1.2,\"mem\":123,\"fd\":456,\"th\":789,\"lat\":1234,\"load\":5678,\"code\":200,\"ifs\":1,\"ric\":0", getAgentStats(stats)) - - stats.RunInContainer = nil - assert.Equal(t, "\"cpu\":1.2,\"mem\":123,\"fd\":456,\"th\":789,\"lat\":1234,\"load\":5678,\"code\":200,\"ifs\":1", getAgentStats(stats)) - - stats.ImdsFallbackSucceed = nil - assert.Equal(t, "\"cpu\":1.2,\"mem\":123,\"fd\":456,\"th\":789,\"lat\":1234,\"load\":5678,\"code\":200", getAgentStats(stats)) - - stats.CpuPercent = nil - assert.Equal(t, "\"mem\":123,\"fd\":456,\"th\":789,\"lat\":1234,\"load\":5678,\"code\":200", getAgentStats(stats)) - - stats.MemoryBytes = nil - assert.Equal(t, "\"fd\":456,\"th\":789,\"lat\":1234,\"load\":5678,\"code\":200", getAgentStats(stats)) - - stats.FileDescriptorCount = nil - assert.Equal(t, "\"th\":789,\"lat\":1234,\"load\":5678,\"code\":200", getAgentStats(stats)) - - stats.ThreadCount = nil - assert.Equal(t, "\"lat\":1234,\"load\":5678,\"code\":200", getAgentStats(stats)) - - stats.LatencyMillis = nil - assert.Equal(t, "\"load\":5678,\"code\":200", getAgentStats(stats)) - - stats.PayloadBytes = nil - assert.Equal(t, "\"code\":200", getAgentStats(stats)) - - stats.StatusCode = nil - assert.Empty(t, getAgentStats(stats)) - - stats.SharedConfigFallback = aws.Int(1) - assert.Equal(t, "\"scfb\":1", getAgentStats(stats)) -} - -func TestGetStatusCode(t *testing.T) { - assert.EqualValues(t, aws.Int(http.StatusOK), getStatusCode(nil)) - assert.Nil(t, getStatusCode(errors.New(""))) - assert.EqualValues(t, aws.Int(http.StatusBadRequest), getStatusCode(awserr.NewRequestFailure(awserr.New("", "", errors.New("")), http.StatusBadRequest, ""))) -} - -func TestGetUserAgent(t *testing.T) { - assert.Equal(t, "TEST_FULL_VERSION", getUserAgent("TEST_GROUP", "TEST_FULL_VERSION", "TEST_RECEIVERS", "TEST_PROCESSORS", "TEST_EXPORTERS", false)) - - expectedUserAgentRegex := `^TEST_FULL_VERSION ` + - `ID/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12} ` + - `inputs:\(TEST_RECEIVERS\) processors:\(TEST_PROCESSORS\) outputs:\(TEST_EXPORTERS\)$` - assert.Regexp(t, expectedUserAgentRegex, getUserAgent("TEST_GROUP", "TEST_FULL_VERSION", "TEST_RECEIVERS", "TEST_PROCESSORS", "TEST_EXPORTERS", true)) - - expectedUserAgentRegex = `^TEST_FULL_VERSION ` + - `ID/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12} ` + - `inputs:\(TEST_RECEIVERS\) processors:\(TEST_PROCESSORS\) outputs:\(TEST_EXPORTERS container_insights\)$` - assert.Regexp(t, expectedUserAgentRegex, getUserAgent("/aws/containerinsights/test/performance", "TEST_FULL_VERSION", "TEST_RECEIVERS", "TEST_PROCESSORS", "TEST_EXPORTERS", true)) - assert.Regexp(t, expectedUserAgentRegex, getUserAgent("/aws/containerinsights/test/prometheus", "TEST_FULL_VERSION", "TEST_RECEIVERS", "TEST_PROCESSORS", "TEST_EXPORTERS", true)) - - expectedUserAgentRegex = `^TEST_FULL_VERSION ` + - `ID/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12} ` + - `inputs:\(TEST_RECEIVERS\) processors:\(TEST_PROCESSORS\)$` - assert.Regexp(t, expectedUserAgentRegex, getUserAgent("TEST_GROUP", "TEST_FULL_VERSION", "TEST_RECEIVERS", "TEST_PROCESSORS", "", true)) - - expectedUserAgentRegex = `^TEST_FULL_VERSION ` + - `ID/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12} ` + - `inputs:\(TEST_RECEIVERS\) outputs:\(TEST_EXPORTERS\)$` - assert.Regexp(t, expectedUserAgentRegex, getUserAgent("TEST_GROUP", "TEST_FULL_VERSION", "TEST_RECEIVERS", "", "TEST_EXPORTERS", true)) - - expectedUserAgentRegex = `^TEST_FULL_VERSION ` + - `ID/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12} ` + - `processors:\(TEST_PROCESSORS\) outputs:\(TEST_EXPORTERS\)$` - assert.Regexp(t, expectedUserAgentRegex, getUserAgent("TEST_GROUP", "TEST_FULL_VERSION", "", "TEST_PROCESSORS", "TEST_EXPORTERS", true)) - - expectedUserAgentRegex = `^TEST_FULL_VERSION ` + - `ID/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$` - assert.Regexp(t, expectedUserAgentRegex, getUserAgent("TEST_GROUP", "TEST_FULL_VERSION", "", "", "", true)) - - t.Setenv(envconfig.CWAGENT_USER_AGENT, "TEST_USER_AGENT") - assert.Equal(t, "TEST_USER_AGENT", getUserAgent("", "", "", "", "", false)) -} - -func TestGetVersion(t *testing.T) { - expectedVersion := "Unknown" - expectedFullVersion := fmt.Sprintf("CWAgent/%s (%s; %s; %s)", - expectedVersion, - runtime.Version(), - runtime.GOOS, - runtime.GOARCH) - assert.Equal(t, expectedVersion, Version()) - assert.Equal(t, expectedFullVersion, FullVersion()) - - ex, err := os.Executable() - require.NoError(t, err) - - expectedVersion = "TEST_VERSION" - expectedFullVersion = fmt.Sprintf("CWAgent/%s (%s; %s; %s)", - expectedVersion, - runtime.Version(), - runtime.GOOS, - runtime.GOARCH) - vfp := filepath.Join(filepath.Dir(ex), versionFilename) - err = os.WriteFile(vfp, []byte(expectedVersion), 0644) - require.NoError(t, err) - defer os.Remove(vfp) - - actualVersion := readVersionFile() - assert.Equal(t, expectedVersion, actualVersion) - assert.Equal(t, expectedFullVersion, getFullVersion(actualVersion)) -} - -func TestIsUsageDataEnabled(t *testing.T) { - assert.True(t, getUsageDataEnabled()) - - t.Setenv(envconfig.CWAGENT_USAGE_DATA, "TRUE") - assert.True(t, getUsageDataEnabled()) - - t.Setenv(envconfig.CWAGENT_USAGE_DATA, "INVALID") - assert.True(t, getUsageDataEnabled()) - - t.Setenv(envconfig.CWAGENT_USAGE_DATA, "FALSE") - assert.False(t, getUsageDataEnabled()) -} - -func TestSharedConfigFallback(t *testing.T) { - defer sharedConfigFallback.Store(false) - assert.Nil(t, getSharedConfigFallback()) - RecordSharedConfigFallback() - assert.Equal(t, 1, *(getSharedConfigFallback())) - RecordSharedConfigFallback() - RecordSharedConfigFallback() - assert.Equal(t, 1, *(getSharedConfigFallback())) -} - -func TestSetImdsFallbackSucceed(t *testing.T) { - defer imdsFallbackSucceed.Store(false) - assert.False(t, imdsFallbackSucceed.Load()) - assert.Nil(t, succeedImdsFallback()) - SetImdsFallbackSucceed() - assert.True(t, imdsFallbackSucceed.Load()) - assert.Equal(t, aws.Int(1), succeedImdsFallback()) -} diff --git a/translator/config/mode.go b/translator/config/mode.go index 8a4294d13b..95638ba471 100644 --- a/translator/config/mode.go +++ b/translator/config/mode.go @@ -9,3 +9,9 @@ const ( ModeOnPremise = "onPremise" ModeWithIRSA = "withIRSA" ) + +const ( + ShortModeEC2 = "EC2" + ShortModeOnPrem = "OP" + ShortModeWithIRSA = "WI" +) diff --git a/translator/config/regiontype.go b/translator/config/regiontype.go new file mode 100644 index 0000000000..889209d9e2 --- /dev/null +++ b/translator/config/regiontype.go @@ -0,0 +1,12 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT + +package config + +const ( + RegionTypeAgentConfigJson = "ACJ" + RegionTypeCredsMap = "CM" + RegionTypeEC2Metadata = "EC2M" + RegionTypeECSMetadata = "ECSM" + RegionTypeNotFound = "RNF" +) diff --git a/translator/context/context.go b/translator/context/context.go index d39daff130..9dcc7ae7a1 100644 --- a/translator/context/context.go +++ b/translator/context/context.go @@ -7,7 +7,6 @@ import ( "log" "os" - "github.com/aws/amazon-cloudwatch-agent/handlers/agentinfo" "github.com/aws/amazon-cloudwatch-agent/translator/config" ) @@ -118,16 +117,16 @@ func (ctx *Context) SetMode(mode string) { switch mode { case config.ModeEC2: ctx.mode = config.ModeEC2 - ctx.shortMode = agentinfo.ModeEC2 + ctx.shortMode = config.ShortModeEC2 case config.ModeOnPrem: ctx.mode = config.ModeOnPrem - ctx.shortMode = agentinfo.ModeOnPrem + ctx.shortMode = config.ShortModeOnPrem case config.ModeOnPremise: ctx.mode = config.ModeOnPremise - ctx.shortMode = agentinfo.ModeOnPrem + ctx.shortMode = config.ShortModeOnPrem case config.ModeWithIRSA: ctx.mode = config.ModeWithIRSA - ctx.shortMode = agentinfo.ModeWithIRSA + ctx.shortMode = config.ShortModeWithIRSA default: log.Panicf("Invalid mode %s. Valid mode values are %s, %s, %s and %s.", mode, config.ModeEC2, config.ModeOnPrem, config.ModeOnPremise, config.ModeWithIRSA) } diff --git a/translator/translate/agent/ruleRegion.go b/translator/translate/agent/ruleRegion.go index dddfd32bba..29981165bd 100644 --- a/translator/translate/agent/ruleRegion.go +++ b/translator/translate/agent/ruleRegion.go @@ -6,8 +6,8 @@ package agent import ( "fmt" - "github.com/aws/amazon-cloudwatch-agent/handlers/agentinfo" "github.com/aws/amazon-cloudwatch-agent/translator" + "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/context" "github.com/aws/amazon-cloudwatch-agent/translator/util" ) @@ -27,7 +27,7 @@ func (r *Region) ApplyRule(input interface{}) (returnKey string, returnVal inter _, inputRegion := translator.DefaultCase(RegionKey, "", input) if inputRegion != "" { Global_Config.Region = inputRegion.(string) - Global_Config.RegionType = agentinfo.AgentConfigJson + Global_Config.RegionType = config.RegionTypeAgentConfigJson return } region, regionType := util.DetectRegion(ctx.Mode(), ctx.Credentials()) diff --git a/translator/util/sdkutil.go b/translator/util/sdkutil.go index de5d5a27f0..c17fdec5cc 100644 --- a/translator/util/sdkutil.go +++ b/translator/util/sdkutil.go @@ -13,7 +13,6 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/amazon-cloudwatch-agent/cfg/commonconfig" - "github.com/aws/amazon-cloudwatch-agent/handlers/agentinfo" "github.com/aws/amazon-cloudwatch-agent/translator" "github.com/aws/amazon-cloudwatch-agent/translator/config" "github.com/aws/amazon-cloudwatch-agent/translator/util/ec2util" @@ -100,23 +99,23 @@ func defaultECSRegion() string { func detectRegion(mode string, credsConfig map[string]string) (region string, regionType string) { region = SDKRegionWithCredsMap(mode, credsConfig) - regionType = agentinfo.RegionNotFound + regionType = config.RegionTypeNotFound if region != "" { - regionType = agentinfo.CredsMap + regionType = config.RegionTypeCredsMap } // For ec2, fallback to metadata when no region info found in credential profile. if region == "" && mode == config.ModeEC2 { fmt.Println("I! Trying to detect region from ec2") region = DefaultEC2Region() - regionType = agentinfo.EC2Metadata + regionType = config.RegionTypeEC2Metadata } // try to get region from ecs metadata if region == "" && mode == config.ModeEC2 { fmt.Println("I! Trying to detect region from ecs") region = DefaultECSRegion() - regionType = agentinfo.ECSMetadata + regionType = config.RegionTypeECSMetadata } return