mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
543 lines
16 KiB
Go
543 lines
16 KiB
Go
package forwarder
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
"testing"
|
|
|
|
"github.com/go-kit/log"
|
|
dslog "github.com/grafana/dskit/log"
|
|
"github.com/stretchr/testify/require"
|
|
"go.opentelemetry.io/collector/pdata/ptrace"
|
|
)
|
|
|
|
type mockWorkingOverrides struct {
|
|
tenantIDs []string
|
|
tenantIDToForwarders map[string][]string
|
|
}
|
|
|
|
func (m *mockWorkingOverrides) TenantIDs() []string {
|
|
return m.tenantIDs
|
|
}
|
|
|
|
func (m *mockWorkingOverrides) Forwarders(tenantID string) []string {
|
|
return m.tenantIDToForwarders[tenantID]
|
|
}
|
|
|
|
type mockWorkingForwarder struct{}
|
|
|
|
func (m *mockWorkingForwarder) ForwardTraces(_ context.Context, _ ptrace.Traces) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockWorkingForwarder) Shutdown(_ context.Context) error {
|
|
return nil
|
|
}
|
|
|
|
type mockFailingForwarder struct {
|
|
forwardTracesErr error
|
|
shutdownErr error
|
|
}
|
|
|
|
func (m *mockFailingForwarder) ForwardTraces(_ context.Context, _ ptrace.Traces) error {
|
|
return m.forwardTracesErr
|
|
}
|
|
|
|
func (m *mockFailingForwarder) Shutdown(_ context.Context) error {
|
|
return m.shutdownErr
|
|
}
|
|
|
|
type mockChannelledInterceptorForwarder struct {
|
|
next Forwarder
|
|
traces chan ptrace.Traces
|
|
}
|
|
|
|
func (m *mockChannelledInterceptorForwarder) ForwardTraces(ctx context.Context, traces ptrace.Traces) error {
|
|
m.traces <- traces
|
|
return m.next.ForwardTraces(ctx, traces)
|
|
}
|
|
|
|
func (m *mockChannelledInterceptorForwarder) Shutdown(ctx context.Context) error {
|
|
return m.next.Shutdown(ctx)
|
|
}
|
|
|
|
func newManagerWithForwarders(t *testing.T, forwarderNameToForwarder map[string]Forwarder, logger log.Logger, o Overrides) *Manager {
|
|
t.Helper()
|
|
|
|
manager, err := NewManager(ConfigList{}, logger, o, dslog.Level{})
|
|
require.NoError(t, err)
|
|
manager.forwarderNameToForwarder = forwarderNameToForwarder
|
|
|
|
require.NoError(t, manager.start(context.Background()))
|
|
t.Cleanup(func() {
|
|
require.NoError(t, manager.stop(nil))
|
|
})
|
|
|
|
return manager
|
|
}
|
|
|
|
func TestNewManager_ReturnsNoErrorAndNonNilManagerWithValidConfigList(t *testing.T) {
|
|
// Given
|
|
cfgs := ConfigList{}
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{}
|
|
|
|
// When
|
|
got, err := NewManager(cfgs, logger, o, dslog.Level{})
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.NotNil(t, got)
|
|
}
|
|
|
|
func TestNewManager_ReturnsErrorAndNilManagerWithInvalidConfigList(t *testing.T) {
|
|
// Given
|
|
cfgs := ConfigList{
|
|
Config{Backend: "unknown"},
|
|
}
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{}
|
|
|
|
// When
|
|
got, err := NewManager(cfgs, logger, o, dslog.Level{})
|
|
|
|
// Then
|
|
require.Error(t, err)
|
|
require.Nil(t, got)
|
|
}
|
|
|
|
func TestManager_ForTenant_ReturnsSingleForwarderWhenSingleForwarderForTenantConfigured(t *testing.T) {
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{"testTenantID"},
|
|
tenantIDToForwarders: map[string][]string{
|
|
"testTenantID": {"testForwarder"},
|
|
},
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder": &mockWorkingForwarder{},
|
|
}
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
forwarderList := manager.ForTenant("testTenantID")
|
|
|
|
// Then
|
|
require.Len(t, forwarderList, 1)
|
|
}
|
|
|
|
func TestManager_ForTenant_ReturnsTwoForwardersWhenTwoForwarderForTenantConfigured(t *testing.T) {
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{"testTenantID"},
|
|
tenantIDToForwarders: map[string][]string{
|
|
"testTenantID": {"testForwarder1", "testForwarder2"},
|
|
},
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder1": &mockWorkingForwarder{},
|
|
"testForwarder2": &mockWorkingForwarder{},
|
|
}
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
forwarderList := manager.ForTenant("testTenantID")
|
|
|
|
// Then
|
|
require.Len(t, forwarderList, 2)
|
|
}
|
|
|
|
func TestManager_ForTenant_ReturnsEmptySliceWhenNoForwardersForTenantConfigured(t *testing.T) {
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{"testTenantID"},
|
|
tenantIDToForwarders: map[string][]string{},
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder1": &mockWorkingForwarder{},
|
|
"testForwarder2": &mockWorkingForwarder{},
|
|
}
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
forwarderList := manager.ForTenant("testTenantID")
|
|
|
|
// Then
|
|
require.Empty(t, forwarderList)
|
|
}
|
|
|
|
func TestManager_ForTenant_ReturnsEmptySliceWhenTenantNotConfigured(t *testing.T) {
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{},
|
|
tenantIDToForwarders: map[string][]string{},
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder1": &mockWorkingForwarder{},
|
|
"testForwarder2": &mockWorkingForwarder{},
|
|
}
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
forwarderList := manager.ForTenant("testTenantID")
|
|
|
|
// Then
|
|
require.Empty(t, forwarderList)
|
|
}
|
|
|
|
func TestManager_ForTenant_List_ForwardTraces_ReturnsNoErrorAndCorrectlyForwardsBatchesToSingleForwarder(t *testing.T) {
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{"testTenantID"},
|
|
tenantIDToForwarders: map[string][]string{
|
|
"testTenantID": {"testForwarder"},
|
|
},
|
|
}
|
|
|
|
forwarderCh := make(chan ptrace.Traces)
|
|
forwarder := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarderCh,
|
|
}
|
|
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder": forwarder,
|
|
}
|
|
|
|
traces := ptrace.NewTraces()
|
|
traces.ResourceSpans().AppendEmpty().SetSchemaUrl("testURL")
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
err := manager.ForTenant("testTenantID").ForwardTraces(context.Background(), traces)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Equal(t, traces, <-forwarderCh)
|
|
}
|
|
|
|
func TestManager_ForTenant_List_ForwardTraces_ReturnsNoErrorAndCorrectlyForwardsBatchesToMultipleForwarders(t *testing.T) {
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{"testTenantID"},
|
|
tenantIDToForwarders: map[string][]string{
|
|
"testTenantID": {"testForwarder1", "testForwarder2"},
|
|
},
|
|
}
|
|
|
|
forwarder1Ch := make(chan ptrace.Traces)
|
|
forwarder1 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder1Ch,
|
|
}
|
|
forwarder2Ch := make(chan ptrace.Traces)
|
|
forwarder2 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder2Ch,
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder1": forwarder1,
|
|
"testForwarder2": forwarder2,
|
|
}
|
|
traces := ptrace.NewTraces()
|
|
traces.ResourceSpans().AppendEmpty().SetSchemaUrl("testURL")
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
err := manager.ForTenant("testTenantID").ForwardTraces(context.Background(), traces)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Equal(t, traces, <-forwarder1Ch)
|
|
require.Equal(t, traces, <-forwarder2Ch)
|
|
}
|
|
|
|
func TestManager_ForTenant_List_ForwardTraces_ReturnsNoErrorAndCorrectlyForwardsBatchesToProperForwardersWhenNewForwarderIsAddedToOverridesConfig(t *testing.T) {
|
|
// Step 1 - Setup manager with two forwarders for tenant and verify that both forwarders receive the trace.
|
|
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{"testTenantID"},
|
|
tenantIDToForwarders: map[string][]string{
|
|
"testTenantID": {"testForwarder1", "testForwarder2"},
|
|
},
|
|
}
|
|
|
|
forwarder1Ch := make(chan ptrace.Traces)
|
|
forwarder1 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder1Ch,
|
|
}
|
|
forwarder2Ch := make(chan ptrace.Traces)
|
|
forwarder2 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder2Ch,
|
|
}
|
|
forwarder3Ch := make(chan ptrace.Traces)
|
|
forwarder3 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder3Ch,
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder1": forwarder1,
|
|
"testForwarder2": forwarder2,
|
|
"testForwarder3": forwarder3,
|
|
}
|
|
traces1 := ptrace.NewTraces()
|
|
traces1.ResourceSpans().AppendEmpty().SetSchemaUrl("testURL1")
|
|
traces2 := ptrace.NewTraces()
|
|
traces2.ResourceSpans().AppendEmpty().SetSchemaUrl("testURL2")
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
err := manager.ForTenant("testTenantID").ForwardTraces(context.Background(), traces1)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Equal(t, traces1, <-forwarder1Ch)
|
|
require.Equal(t, traces1, <-forwarder2Ch)
|
|
require.Len(t, forwarder3Ch, 0)
|
|
|
|
// Step 2 - Add additional forwarder, simulate "tick" and verify that all three forwarders receive the trace.
|
|
currentForwardersForTenant := o.tenantIDToForwarders["testTenantID"]
|
|
o.tenantIDToForwarders["testTenantID"] = append(currentForwardersForTenant, "testForwarder3")
|
|
manager.updateQueueLists()
|
|
|
|
// When
|
|
err = manager.ForTenant("testTenantID").ForwardTraces(context.Background(), traces2)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Equal(t, traces2, <-forwarder1Ch)
|
|
require.Equal(t, traces2, <-forwarder2Ch)
|
|
require.Equal(t, traces2, <-forwarder3Ch)
|
|
}
|
|
|
|
func TestManager_ForTenant_List_ForwardTraces_ReturnsNoErrorAndCorrectlyForwardsBatchesToProperForwardersWhenOldForwarderIsRemovedFromOverridesConfig(t *testing.T) {
|
|
// Step 1 - Setup manager with three forwarders for tenant and verify that all three forwarders receive the trace.
|
|
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{"testTenantID"},
|
|
tenantIDToForwarders: map[string][]string{
|
|
"testTenantID": {"testForwarder1", "testForwarder2", "testForwarder3"},
|
|
},
|
|
}
|
|
|
|
forwarder1Ch := make(chan ptrace.Traces)
|
|
forwarder1 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder1Ch,
|
|
}
|
|
forwarder2Ch := make(chan ptrace.Traces)
|
|
forwarder2 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder2Ch,
|
|
}
|
|
forwarder3Ch := make(chan ptrace.Traces)
|
|
forwarder3 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder3Ch,
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder1": forwarder1,
|
|
"testForwarder2": forwarder2,
|
|
"testForwarder3": forwarder3,
|
|
}
|
|
traces1 := ptrace.NewTraces()
|
|
traces1.ResourceSpans().AppendEmpty().SetSchemaUrl("testURL1")
|
|
traces2 := ptrace.NewTraces()
|
|
traces2.ResourceSpans().AppendEmpty().SetSchemaUrl("testURL2")
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
err := manager.ForTenant("testTenantID").ForwardTraces(context.Background(), traces1)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Equal(t, traces1, <-forwarder1Ch)
|
|
require.Equal(t, traces1, <-forwarder2Ch)
|
|
require.Equal(t, traces1, <-forwarder3Ch)
|
|
|
|
// Step 2 - Remove one forwarder, simulate "tick" and verify that remaining forwarders receive the trace.
|
|
idx := slices.Index(o.tenantIDToForwarders["testTenantID"], "testForwarder2")
|
|
require.NotEqual(t, -1, idx)
|
|
slices.Delete(o.tenantIDToForwarders["testTenantID"], idx, idx)
|
|
manager.updateQueueLists()
|
|
|
|
// When
|
|
err = manager.ForTenant("testTenantID").ForwardTraces(context.Background(), traces2)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Equal(t, traces2, <-forwarder1Ch)
|
|
require.Len(t, forwarder2Ch, 0)
|
|
require.Equal(t, traces2, <-forwarder3Ch)
|
|
}
|
|
|
|
func TestManager_ForTenant_List_ForwardTraces_ReturnsNoErrorAndCorrectlyForwardsBatchesToMultipleForwardersForMultipleTenants(t *testing.T) {
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{"testTenantID1", "testTenantID2"},
|
|
tenantIDToForwarders: map[string][]string{
|
|
"testTenantID1": {"testForwarder1", "testForwarder2"},
|
|
"testTenantID2": {"testForwarder2"},
|
|
},
|
|
}
|
|
|
|
forwarder1Ch := make(chan ptrace.Traces)
|
|
forwarder1 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder1Ch,
|
|
}
|
|
forwarder2Ch := make(chan ptrace.Traces)
|
|
forwarder2 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder2Ch,
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder1": forwarder1,
|
|
"testForwarder2": forwarder2,
|
|
}
|
|
traces := ptrace.NewTraces()
|
|
traces.ResourceSpans().AppendEmpty().SetSchemaUrl("testURL")
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
err := manager.ForTenant("testTenantID1").ForwardTraces(context.Background(), traces)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Equal(t, traces, <-forwarder1Ch)
|
|
require.Equal(t, traces, <-forwarder2Ch)
|
|
|
|
// When
|
|
err = manager.ForTenant("testTenantID2").ForwardTraces(context.Background(), traces)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Len(t, forwarder1Ch, 0)
|
|
require.Equal(t, traces, <-forwarder2Ch)
|
|
}
|
|
|
|
func TestManager_ForTenant_List_ForwardTraces_ReturnsNoErrorAndCorrectlyForwardsBatchesToProperForwardersWhenNewTenantIsAddedToOverridesConfig(t *testing.T) {
|
|
// Step 1 - Setup manager with no tenants and verify that no batches are being forwarded
|
|
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{},
|
|
tenantIDToForwarders: map[string][]string{},
|
|
}
|
|
|
|
forwarder1Ch := make(chan ptrace.Traces)
|
|
forwarder1 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder1Ch,
|
|
}
|
|
forwarder2Ch := make(chan ptrace.Traces)
|
|
forwarder2 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder2Ch,
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder1": forwarder1,
|
|
"testForwarder2": forwarder2,
|
|
}
|
|
traces := ptrace.NewTraces()
|
|
traces.ResourceSpans().AppendEmpty().SetSchemaUrl("testURL")
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
forwarderList := manager.ForTenant("testTenantID")
|
|
err := forwarderList.ForwardTraces(context.Background(), traces)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Len(t, forwarderList, 0)
|
|
require.Len(t, forwarder1Ch, 0)
|
|
require.Len(t, forwarder2Ch, 0)
|
|
|
|
// Step 2 - Add tenant to overrides config, simulate "tick", and verify that both forwarders receive the traces.
|
|
o.tenantIDs = []string{"testTenantID"}
|
|
o.tenantIDToForwarders = map[string][]string{
|
|
"testTenantID": {"testForwarder1", "testForwarder2"},
|
|
}
|
|
manager.updateQueueLists()
|
|
|
|
// When
|
|
forwarderList = manager.ForTenant("testTenantID")
|
|
err = forwarderList.ForwardTraces(context.Background(), traces)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Len(t, forwarderList, 2)
|
|
require.Equal(t, traces, <-forwarder1Ch)
|
|
require.Equal(t, traces, <-forwarder2Ch)
|
|
}
|
|
|
|
func TestManager_ForTenant_List_ForwardTraces_ReturnsNoErrorAndCorrectlyDoesNotForwardTracesToForwardersWhenTenantIsRemovedFromOverridesConfig(t *testing.T) {
|
|
// Step 1 - Setup manager with two forwarders for tenant and verify that both forwarders receive the trace.
|
|
|
|
// Given
|
|
logger := log.NewNopLogger()
|
|
o := &mockWorkingOverrides{
|
|
tenantIDs: []string{"testTenantID"},
|
|
tenantIDToForwarders: map[string][]string{
|
|
"testTenantID": {"testForwarder1", "testForwarder2"},
|
|
},
|
|
}
|
|
|
|
forwarder1Ch := make(chan ptrace.Traces)
|
|
forwarder1 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder1Ch,
|
|
}
|
|
forwarder2Ch := make(chan ptrace.Traces)
|
|
forwarder2 := &mockChannelledInterceptorForwarder{
|
|
next: &mockWorkingForwarder{},
|
|
traces: forwarder2Ch,
|
|
}
|
|
forwarderNameToForwarder := map[string]Forwarder{
|
|
"testForwarder1": forwarder1,
|
|
"testForwarder2": forwarder2,
|
|
}
|
|
traces := ptrace.NewTraces()
|
|
traces.ResourceSpans().AppendEmpty().SetSchemaUrl("testURL")
|
|
manager := newManagerWithForwarders(t, forwarderNameToForwarder, logger, o)
|
|
|
|
// When
|
|
forwarderList := manager.ForTenant("testTenantID")
|
|
err := forwarderList.ForwardTraces(context.Background(), traces)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Len(t, forwarderList, 2)
|
|
require.Equal(t, traces, <-forwarder1Ch)
|
|
require.Equal(t, traces, <-forwarder2Ch)
|
|
|
|
// Step 2 - Remove forwarder from overrides config and verify that the traces are no longer forwarded.
|
|
o.tenantIDs = []string{}
|
|
o.tenantIDToForwarders = map[string][]string{}
|
|
manager.updateQueueLists()
|
|
|
|
// When
|
|
forwarderList = manager.ForTenant("testTenantID")
|
|
err = forwarderList.ForwardTraces(context.Background(), traces)
|
|
|
|
// Then
|
|
require.NoError(t, err)
|
|
require.Len(t, forwarderList, 0)
|
|
require.Len(t, forwarder1Ch, 0)
|
|
require.Len(t, forwarder2Ch, 0)
|
|
}
|