Skip to content
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

feat: drop fleet agentcfg and direct fetcher #14921

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 30 additions & 61 deletions internal/agentcfg/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,46 @@

package agentcfg

import (
"context"

"github.com/elastic/apm-server/internal/beater/config"
)

// TransactionSamplingRateKey is the agent configuration key for the
// sampling rate. This is used by the Jaeger handler to adapt our agent
// configuration to the Jaeger remote sampler protocol.
const TransactionSamplingRateKey = "transaction_sample_rate"
import "context"

// Fetcher defines a common interface to retrieving agent config.
type Fetcher interface {
Fetch(context.Context, Query) (Result, error)
}

// DirectFetcher is an agent config fetcher which serves requests out of a
// statically defined set of agent configuration. These configurations are
// typically provided via Fleet.
type DirectFetcher struct {
cfgs []AgentConfig
// AgentConfig holds an agent configuration definition.
type AgentConfig struct {
// ServiceName holds the service name to which this agent configuration
// applies. This is optional.
ServiceName string

// ServiceEnvironment holds the service environment to which this agent
// configuration applies. This is optional.
ServiceEnvironment string

// AgentName holds the agent name to which this agent configuration
// applies. This is optional, and is used for filtering configuration
// settings for unauthenticated agents.
AgentName string

// Etag holds a unique ID for the configuration, which agents
// will send along with their queries. The server uses this to
// determine whether agent configuration has been applied.
Etag string

// Config holds configuration settings that should be sent to
// agents matching the above constraints.
Config map[string]string
}

// NewDirectFetcher returns a new DirectFetcher that serves agent configuration
// requests using cfgs.
func NewDirectFetcher(cfgs []AgentConfig) *DirectFetcher {
return &DirectFetcher{cfgs}
func NewEmptyFetcher() Fetcher {
return &emptyFetcher{}
}

// Fetch finds a matching AgentConfig in cfgs based on the received Query.
func (f *DirectFetcher) Fetch(_ context.Context, query Query) (Result, error) {
return matchAgentConfig(query, f.cfgs), nil
type emptyFetcher struct{}

func (*emptyFetcher) Fetch(context.Context, Query) (Result, error) {
return zeroResult(), nil
}

// matchAgentConfig finds a matching AgentConfig based on the received Query.
Expand Down Expand Up @@ -97,42 +105,3 @@ func matchAgentConfig(query Query, cfgs []AgentConfig) Result {
}
return result
}

// AgentConfig holds an agent configuration definition.
type AgentConfig struct {
// ServiceName holds the service name to which this agent configuration
// applies. This is optional.
ServiceName string

// ServiceEnvironment holds the service environment to which this agent
// configuration applies. This is optional.
ServiceEnvironment string

// AgentName holds the agent name to which this agent configuration
// applies. This is optional, and is used for filtering configuration
// settings for unauthenticated agents.
AgentName string

// Etag holds a unique ID for the configuration, which agents
// will send along with their queries. The server uses this to
// determine whether agent configuration has been applied.
Etag string

// Config holds configuration settings that should be sent to
// agents matching the above constraints.
Config map[string]string
}

func ConvertAgentConfigs(fleetAgentConfigs []config.FleetAgentConfig) []AgentConfig {
agentConfigurations := make([]AgentConfig, len(fleetAgentConfigs))
for i, in := range fleetAgentConfigs {
agentConfigurations[i] = AgentConfig{
ServiceName: in.Service.Name,
ServiceEnvironment: in.Service.Environment,
AgentName: in.AgentName,
Etag: in.Etag,
Config: in.Config,
}
}
return agentConfigurations
}
213 changes: 0 additions & 213 deletions internal/agentcfg/fetch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@
package agentcfg

import (
"context"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var (
Expand All @@ -38,214 +36,3 @@ func TestCustomJSON(t *testing.T) {
actual, _ := newResult([]byte(input), nil)
assert.Equal(t, expected, actual)
}

func TestDirectConfigurationPrecedence(t *testing.T) {
for _, tc := range []struct {
query Query
agentConfigs []AgentConfig
expectedSettings map[string]string
}{
{
query: Query{
Service: Service{
Name: "service1",
Environment: "production",
},
},
agentConfigs: []AgentConfig{
{
ServiceEnvironment: "production",
Config: map[string]string{"key1": "val2", "key2": "val2"},
Etag: "def456",
},
{
ServiceName: "service1",
Config: map[string]string{"key3": "val3"},
Etag: "abc123",
},
{
ServiceName: "service1",
ServiceEnvironment: "production",
Config: map[string]string{"key1": "val1"},
Etag: "abc123",
},
},
expectedSettings: map[string]string{
"key1": "val1",
},
},
{
query: Query{
Service: Service{
Name: "service1",
Environment: "production",
},
},
agentConfigs: []AgentConfig{
{
ServiceEnvironment: "production",
Config: map[string]string{"key3": "val3"},
Etag: "def456",
},
{
ServiceName: "service1",
Config: map[string]string{"key1": "val1", "key2": "val2"},
Etag: "abc123",
},
},
expectedSettings: map[string]string{
"key1": "val1",
"key2": "val2",
},
},
{
query: Query{
InsecureAgents: []string{"Jaeger"},
Service: Service{
Name: "service1",
Environment: "production",
},
},
agentConfigs: []AgentConfig{
{
ServiceEnvironment: "production",
Config: map[string]string{"key3": "val3"},
Etag: "def456",
},
{
ServiceName: "service1",
Config: map[string]string{"key1": "val1", "key2": "val2"},
Etag: "abc123",
},
},
expectedSettings: map[string]string{
"key1": "val1",
"key2": "val2",
},
},
{
query: Query{
InsecureAgents: []string{"Jaeger"},
Service: Service{
Name: "service1",
Environment: "production",
},
},
agentConfigs: []AgentConfig{
{
ServiceEnvironment: "production",
Config: map[string]string{"key3": "val3"},
Etag: "def456",
},
{
ServiceName: "service1",
AgentName: "Jaeger/Python",
Config: map[string]string{"key1": "val1", "key2": "val2", "transaction_sample_rate": "0.1"},
Etag: "abc123",
},
},
expectedSettings: map[string]string{
"key1": "val1",
"key2": "val2",
"transaction_sample_rate": "0.1",
},
},
{
query: Query{
Service: Service{
Name: "service1",
Environment: "production",
},
},
agentConfigs: []AgentConfig{
{
ServiceName: "service2",
Config: map[string]string{"key1": "val1", "key2": "val2"},
Etag: "abc123",
},
{
ServiceEnvironment: "production",
Config: map[string]string{"key3": "val3"},
Etag: "def456",
},
},
expectedSettings: map[string]string{
"key3": "val3",
},
},
{
query: Query{
Service: Service{
Name: "service1",
Environment: "production",
},
},
agentConfigs: []AgentConfig{
{
ServiceName: "not-found",
Config: map[string]string{"key1": "val1"},
Etag: "abc123",
},
},
expectedSettings: map[string]string{},
},
{
query: Query{
Service: Service{
Name: "service2",
Environment: "production",
},
},
agentConfigs: []AgentConfig{
{
ServiceName: "service1",
Config: map[string]string{"key1": "val1", "key2": "val2"},
Etag: "abc123",
},
{
ServiceName: "service2",
Config: map[string]string{"key1": "val4", "key2": "val5"},
Etag: "abc123",
},
},
expectedSettings: map[string]string{
"key1": "val4",
"key2": "val5",
},
},
{
query: Query{
Service: Service{
Name: "service2",
Environment: "staging",
},
},
agentConfigs: []AgentConfig{
{
ServiceName: "service1",
Config: map[string]string{"key1": "val1", "key2": "val2"},
Etag: "abc123",
},
{
ServiceEnvironment: "production",
Config: map[string]string{"key1": "val4", "key2": "val5"},
Etag: "abc123",
},
{
Config: map[string]string{"key3": "val5", "key4": "val6"},
Etag: "abc123",
},
},
expectedSettings: map[string]string{
"key3": "val5",
"key4": "val6",
},
},
} {
f := NewDirectFetcher(tc.agentConfigs)
result, err := f.Fetch(context.Background(), tc.query)
require.NoError(t, err)

assert.Equal(t, Settings(tc.expectedSettings), result.Source.Settings)
}
}
17 changes: 0 additions & 17 deletions internal/beater/api/config/agent/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,23 +231,6 @@ func TestAgentConfigHandlerAuthorizedForService(t *testing.T) {
assert.False(t, called)
}

func TestConfigAgentHandler_DirectConfiguration(t *testing.T) {
fetcher := agentcfg.NewDirectFetcher([]agentcfg.AgentConfig{{
ServiceName: "service1",
Config: map[string]string{"key1": "val1"},
Etag: "abc123",
}})
h := NewHandler(fetcher, time.Nanosecond, "", nil)

w := sendRequest(h, httptest.NewRequest(http.MethodPost, "/config", jsonReader(map[string]interface{}{
"service": map[string]interface{}{
"name": "service1",
},
})))
assert.Equal(t, http.StatusOK, w.Code, w.Body.String())
assert.JSONEq(t, `{"key1":"val1"}`, w.Body.String())
}

func TestAgentConfigHandler_PostOk(t *testing.T) {
f := newSanitizingKibanaFetcher(t, func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `{"_id": "1", "_source": {"settings": {"sampling_rate": 0.5}}}`)
Expand Down
2 changes: 1 addition & 1 deletion internal/beater/api/mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func (m muxBuilder) build(cfg *config.Config) (http.Handler, error) {
cfg,
nopBatchProcessor,
authenticator,
agentcfg.NewDirectFetcher(nil),
agentcfg.NewEmptyFetcher(),
ratelimitStore,
m.SourcemapFetcher,
func() bool { return true },
Expand Down
Loading
Loading