From a3d14ee2d833461a725b924a5d1135bf34194d7b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 07:25:43 +0100 Subject: [PATCH] Support sampling rate in APM configuration (#4037) (#4052) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support sampling rate in APM configuration (cherry picked from commit dbdd0566046fdc0c58dc6e5009ac76ba41ac84c4) Co-authored-by: Paolo ChilĂ  --- internal/pkg/server/agent.go | 7 +++ internal/pkg/server/agent_test.go | 93 +++++++++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/internal/pkg/server/agent.go b/internal/pkg/server/agent.go index 955ac1ada..92f2d63e1 100644 --- a/internal/pkg/server/agent.go +++ b/internal/pkg/server/agent.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "strconv" "strings" "sync" "time" @@ -556,6 +557,12 @@ func apmConfigToInstrumentation(src *proto.APMConfig) (config.Instrumentation, e Hosts: apmest.GetHosts(), GlobalLabels: apmest.GetGlobalLabels(), } + + if apmest.SamplingRate != nil { + // set the sampling rate in config + cfg.TransactionSampleRate = strconv.FormatFloat(float64(*apmest.SamplingRate), 'f', -1, 32) + } + return cfg, nil } return config.Instrumentation{}, fmt.Errorf("unable to transform APMConfig to instrumentation") diff --git a/internal/pkg/server/agent_test.go b/internal/pkg/server/agent_test.go index afed695cd..ff3df0e5d 100644 --- a/internal/pkg/server/agent_test.go +++ b/internal/pkg/server/agent_test.go @@ -239,6 +239,78 @@ func Test_Agent_configFromUnits(t *testing.T) { assert.Equal(t, "fleet-server", cfg.Inputs[0].Type) require.Len(t, cfg.Output.Elasticsearch.Hosts, 2) }) + t.Run("Minimal APM config is specified", func(t *testing.T) { + outStruct, err := structpb.NewStruct(map[string]interface{}{ + "service_token": "test-token", + }) + require.NoError(t, err) + mockOutClient := &mockClientUnit{} + mockOutClient.On("Expected").Return( + client.Expected{ + State: client.UnitStateHealthy, + LogLevel: client.UnitLogLevelInfo, + Config: &proto.UnitExpectedConfig{Source: outStruct}, + }) + + inStruct, err := structpb.NewStruct(map[string]interface{}{ + "type": "fleet-server", + "server": map[string]interface{}{ + "host": "0.0.0.0", + }, + "policy": map[string]interface{}{ + "id": "test-policy", + }, + }) + require.NoError(t, err) + + mockInClient := &mockClientUnit{} + mockInClient.On("Expected").Return( + client.Expected{ + State: client.UnitStateHealthy, + LogLevel: client.UnitLogLevelInfo, + Config: &proto.UnitExpectedConfig{Source: inStruct}, + APMConfig: &proto.APMConfig{ + Elastic: &proto.ElasticAPM{ + Environment: "test", + SecretToken: "secretToken", + Hosts: []string{"testhost:8080"}, + }, + }, + }) + + cliCfg, err := ucfg.NewFrom(map[string]interface{}{ + "inputs": []interface{}{ + map[string]interface{}{ + "policy": map[string]interface{}{ + "id": "test-policy", + }, + }, + }, + }) + require.NoError(t, err) + a := &Agent{ + cliCfg: cliCfg, + agent: mockAgent, + inputUnit: mockInClient, + outputUnit: mockOutClient, + } + + cfg, err := a.configFromUnits(context.Background()) + require.NoError(t, err) + require.Len(t, cfg.Inputs, 1) + assert.Equal(t, "fleet-server", cfg.Inputs[0].Type) + assert.Equal(t, "test-policy", cfg.Inputs[0].Policy.ID) + assert.Equal(t, "0.0.0.0", cfg.Inputs[0].Server.Host) + assert.True(t, cfg.Inputs[0].Server.Instrumentation.Enabled) + assert.False(t, cfg.Inputs[0].Server.Instrumentation.TLS.SkipVerify) + assert.Empty(t, cfg.Inputs[0].Server.Instrumentation.TLS.ServerCA) + assert.Equal(t, "test", cfg.Inputs[0].Server.Instrumentation.Environment) + assert.Empty(t, cfg.Inputs[0].Server.Instrumentation.APIKey) + assert.Equal(t, "secretToken", cfg.Inputs[0].Server.Instrumentation.SecretToken) + assert.Equal(t, []string{"testhost:8080"}, cfg.Inputs[0].Server.Instrumentation.Hosts) + assert.Empty(t, cfg.Inputs[0].Server.Instrumentation.GlobalLabels) + assert.Equal(t, "test-token", cfg.Output.Elasticsearch.ServiceToken) + }) t.Run("APM config is specified", func(t *testing.T) { outStruct, err := structpb.NewStruct(map[string]interface{}{ "service_token": "test-token", @@ -262,6 +334,8 @@ func Test_Agent_configFromUnits(t *testing.T) { }, }) require.NoError(t, err) + + samplingRate := float32(0.5) mockInClient := &mockClientUnit{} mockInClient.On("Expected").Return( client.Expected{ @@ -279,6 +353,7 @@ func Test_Agent_configFromUnits(t *testing.T) { SecretToken: "secretToken", Hosts: []string{"testhost:8080"}, GlobalLabels: "test", + SamplingRate: &samplingRate, }, }, }) @@ -315,6 +390,7 @@ func Test_Agent_configFromUnits(t *testing.T) { assert.Equal(t, []string{"testhost:8080"}, cfg.Inputs[0].Server.Instrumentation.Hosts) assert.Equal(t, "test", cfg.Inputs[0].Server.Instrumentation.GlobalLabels) assert.Equal(t, "test-token", cfg.Output.Elasticsearch.ServiceToken) + assert.Equal(t, "0.5", cfg.Inputs[0].Server.Instrumentation.TransactionSampleRate) }) t.Run("APM config no tls", func(t *testing.T) { outStruct, err := structpb.NewStruct(map[string]interface{}{ @@ -336,6 +412,8 @@ func Test_Agent_configFromUnits(t *testing.T) { }, }) require.NoError(t, err) + + samplingRate := float32(0.01) mockInClient := &mockClientUnit{} mockInClient.On("Expected").Return( client.Expected{ @@ -349,6 +427,7 @@ func Test_Agent_configFromUnits(t *testing.T) { SecretToken: "secretToken", Hosts: []string{"testhost:8080"}, GlobalLabels: "test", + SamplingRate: &samplingRate, }, }, }) @@ -374,6 +453,7 @@ func Test_Agent_configFromUnits(t *testing.T) { assert.Equal(t, []string{"testhost:8080"}, cfg.Inputs[0].Server.Instrumentation.Hosts) assert.Equal(t, "test", cfg.Inputs[0].Server.Instrumentation.GlobalLabels) assert.Equal(t, "test-token", cfg.Output.Elasticsearch.ServiceToken) + assert.Equal(t, "0.01", cfg.Inputs[0].Server.Instrumentation.TransactionSampleRate) }) t.Run("APM config and instrumentation is specified", func(t *testing.T) { outStruct, err := structpb.NewStruct(map[string]interface{}{ @@ -398,14 +478,17 @@ func Test_Agent_configFromUnits(t *testing.T) { "skip_verify": true, "server_certificate": "/path/to/cert.crt", }, - "environment": "replace", - "api_key": "replace", - "secret_token": "replace", - "hosts": []interface{}{"replace"}, + "environment": "replace", + "api_key": "replace", + "secret_token": "replace", + "hosts": []interface{}{"replace"}, + "transaction_sample_rate": "0.75", }, }, }) require.NoError(t, err) + + samplingRate := float32(0.01) mockInClient := &mockClientUnit{} mockInClient.On("Expected").Return( client.Expected{ @@ -423,6 +506,7 @@ func Test_Agent_configFromUnits(t *testing.T) { SecretToken: "secretToken", Hosts: []string{"testhost:8080"}, GlobalLabels: "test", + SamplingRate: &samplingRate, }, }, }) @@ -449,6 +533,7 @@ func Test_Agent_configFromUnits(t *testing.T) { assert.Equal(t, []string{"testhost:8080"}, cfg.Inputs[0].Server.Instrumentation.Hosts) assert.Equal(t, "test", cfg.Inputs[0].Server.Instrumentation.GlobalLabels) assert.Equal(t, "test-token", cfg.Output.Elasticsearch.ServiceToken) + assert.Equal(t, "0.01", cfg.Inputs[0].Server.Instrumentation.TransactionSampleRate) }) t.Run("APM config error", func(t *testing.T) { outStruct, err := structpb.NewStruct(map[string]interface{}{