From 32b067828ffa2b0101911ed1c1e914bfe1e7cf44 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Wed, 3 Apr 2024 09:18:37 +0700 Subject: [PATCH 01/29] Add gRPC integration e2e test mode Signed-off-by: James Ryans --- .github/workflows/ci-grpc.yml | 4 +- Makefile | 2 +- cmd/jaeger/grpc_config.yaml | 4 - .../internal/integration/e2e/grpc_test.go | 103 ++++++++ .../internal/integration/e2e/integration.go | 230 ++++++++++++++++++ plugin/storage/integration/integration.go | 10 +- 6 files changed, 346 insertions(+), 7 deletions(-) create mode 100644 cmd/jaeger/internal/integration/e2e/grpc_test.go create mode 100644 cmd/jaeger/internal/integration/e2e/integration.go diff --git a/.github/workflows/ci-grpc.yml b/.github/workflows/ci-grpc.yml index d059e64c0a7..68165182b15 100644 --- a/.github/workflows/ci-grpc.yml +++ b/.github/workflows/ci-grpc.yml @@ -40,7 +40,9 @@ jobs: make grpc-storage-integration-test ;; v2) - bash scripts/grpc-integration-test.sh latest + STORAGE=grpc \ + SPAN_STORAGE_TYPE=memory \ + make jaeger-storage-integration-test ;; esac diff --git a/Makefile b/Makefile index 40195b4fdaf..b38c4b749ce 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ SHELL := /bin/bash JAEGER_IMPORT_PATH = github.com/jaegertracing/jaeger STORAGE_PKGS = ./plugin/storage/integration/... -JAEGER_V2_STORAGE_PKGS = ./cmd/jaeger/internal/integration +JAEGER_V2_STORAGE_PKGS = ./cmd/jaeger/internal/integration/e2e # These DOCKER_xxx vars are used when building Docker images. DOCKER_NAMESPACE?=jaegertracing diff --git a/cmd/jaeger/grpc_config.yaml b/cmd/jaeger/grpc_config.yaml index c982b796646..0da873500bd 100644 --- a/cmd/jaeger/grpc_config.yaml +++ b/cmd/jaeger/grpc_config.yaml @@ -9,7 +9,6 @@ service: extensions: jaeger_query: trace_storage: external-storage - trace_storage_archive: external-storage-archive ui_config: ./cmd/jaeger/config-ui.json jaeger_storage: @@ -17,9 +16,6 @@ extensions: external-storage: server: localhost:17271 connection-timeout: 5s - external-storage-archive: - server: localhost:17281 - connection-timeout: 5s receivers: otlp: diff --git a/cmd/jaeger/internal/integration/e2e/grpc_test.go b/cmd/jaeger/internal/integration/e2e/grpc_test.go new file mode 100644 index 00000000000..c3a6a78008e --- /dev/null +++ b/cmd/jaeger/internal/integration/e2e/grpc_test.go @@ -0,0 +1,103 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/cmd/remote-storage/app" + "github.com/jaegertracing/jaeger/pkg/config" + "github.com/jaegertracing/jaeger/pkg/healthcheck" + "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/pkg/tenancy" + "github.com/jaegertracing/jaeger/plugin/storage" + "github.com/jaegertracing/jaeger/ports" +) + +type GRPCStorageIntegration struct { + E2EStorageIntegration + + logger *zap.Logger + server *app.Server + storageFactory *storage.Factory +} + +func (s *GRPCStorageIntegration) initialize() error { + err := s.startServer() + if err != nil { + return err + } + + s.Refresh = s.refresh + s.CleanUp = s.cleanUp + return nil +} + +func (s *GRPCStorageIntegration) startServer() error { + opts := &app.Options{ + GRPCHostPort: ports.PortToHostPort(ports.RemoteStorageGRPC), + Tenancy: tenancy.Options{ + Enabled: false, + }, + } + tm := tenancy.NewManager(&opts.Tenancy) + var err error + s.storageFactory, err = storage.NewFactory(storage.FactoryConfigFromEnvAndCLI(os.Args, os.Stderr)) + if err != nil { + return err + } + v, _ := config.Viperize(s.storageFactory.AddFlags) + s.storageFactory.InitFromViper(v, s.logger) + err = s.storageFactory.Initialize(metrics.NullFactory, s.logger) + if err != nil { + return err + } + + s.server, err = app.NewServer(opts, s.storageFactory, tm, s.logger, healthcheck.New()) + if err != nil { + return err + } + err = s.server.Start() + if err != nil { + return err + } + return nil +} + +func (s *GRPCStorageIntegration) refresh() error { + return nil +} + +func (s *GRPCStorageIntegration) cleanUp() error { + if err := s.server.Close(); err != nil { + return err + } + if err := s.storageFactory.Close(); err != nil { + return err + } + return s.initialize() +} + +func TestGRPCStorage(t *testing.T) { + if os.Getenv("STORAGE") != "grpc" { + t.Skip("Integration test against gRPC skipped; set STORAGE env var to grpc to run this") + } + + logger, err := zap.NewDevelopment() + require.NoError(t, err) + + s := &GRPCStorageIntegration{ + logger: logger, + } + require.NoError(t, s.initialize()) + require.NoError(t, s.e2eInitialize()) + t.Cleanup(func() { + require.NoError(t, s.e2eCleanUp()) + }) + s.IntegrationTestSpanstore(t) +} diff --git a/cmd/jaeger/internal/integration/e2e/integration.go b/cmd/jaeger/internal/integration/e2e/integration.go new file mode 100644 index 00000000000..362a9e48e3b --- /dev/null +++ b/cmd/jaeger/internal/integration/e2e/integration.go @@ -0,0 +1,230 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "context" + "errors" + "io" + "os" + "strings" + "time" + + jaeger2otlp "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" + "github.com/open-telemetry/opentelemetry-collector-contrib/testbed/testbed" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + + "github.com/jaegertracing/jaeger/cmd/jaeger/internal" + "github.com/jaegertracing/jaeger/model" + "github.com/jaegertracing/jaeger/plugin/storage/integration" + "github.com/jaegertracing/jaeger/ports" + "github.com/jaegertracing/jaeger/proto-gen/api_v2" + "github.com/jaegertracing/jaeger/storage/spanstore" +) + +var ( + _ spanstore.Writer = (*spanWriter)(nil) + _ spanstore.Reader = (*spanReader)(nil) +) + +type E2EStorageIntegration struct { + integration.StorageIntegration + + runner testbed.OtelcolRunner + configCleanup func() +} + +func (s *E2EStorageIntegration) e2eInitialize() error { + factories, err := internal.Components() + if err != nil { + return err + } + + config, err := os.ReadFile("../../../grpc_config.yaml") + if err != nil { + return err + } + config = []byte(strings.Replace(string(config), "./cmd/jaeger/", "../../../", 1)) + + s.runner = testbed.NewInProcessCollector(factories) + s.configCleanup, err = s.runner.PrepareConfig(string(config)) + if err != nil { + return err + } + if err = s.runner.Start(testbed.StartParams{}); err != nil { + return err + } + + if s.SpanWriter, err = CreateSpanWriter(); err != nil { + return err + } + if s.SpanReader, err = CreateSpanReader(); err != nil { + return err + } + return nil +} + +func (s *E2EStorageIntegration) e2eCleanUp() error { + s.configCleanup() + _, err := s.runner.Stop() + return err +} + +type spanWriter struct { + testbed.TraceDataSender +} + +func CreateSpanWriter() (*spanWriter, error) { + sender := testbed.NewOTLPTraceDataSender(testbed.DefaultHost, testbed.DefaultOTLPPort) + err := sender.Start() + if err != nil { + return nil, err + } + + return &spanWriter{ + TraceDataSender: sender, + }, nil +} + +func (w *spanWriter) WriteSpan(ctx context.Context, span *model.Span) error { + td, err := jaeger2otlp.ProtoToTraces([]*model.Batch{ + { + Spans: []*model.Span{span}, + Process: span.Process, + }, + }) + if err != nil { + return err + } + + return w.ConsumeTraces(ctx, td) +} + +type spanReader struct { + client api_v2.QueryServiceClient +} + +func CreateSpanReader() (*spanReader, error) { + opts := []grpc.DialOption{ + grpc.WithBlock(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + cc, err := grpc.DialContext(ctx, ports.PortToHostPort(ports.QueryGRPC), opts...) + if err != nil { + return nil, err + } + + return &spanReader{ + client: api_v2.NewQueryServiceClient(cc), + }, nil +} + +func unwrapNotFoundErr(err error) error { + if s, _ := status.FromError(err); s != nil { + if strings.Contains(s.Message(), spanstore.ErrTraceNotFound.Error()) { + return spanstore.ErrTraceNotFound + } + } + return err +} + +func (r *spanReader) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { + stream, err := r.client.GetTrace(ctx, &api_v2.GetTraceRequest{ + TraceID: traceID, + }) + if err != nil { + return nil, unwrapNotFoundErr(err) + } + + var spans []*model.Span + for received, err := stream.Recv(); !errors.Is(err, io.EOF); received, err = stream.Recv() { + if err != nil { + return nil, unwrapNotFoundErr(err) + } + for i := range received.Spans { + spans = append(spans, &received.Spans[i]) + } + } + + return &model.Trace{ + Spans: spans, + }, nil +} + +func (r *spanReader) GetServices(ctx context.Context) ([]string, error) { + res, err := r.client.GetServices(ctx, &api_v2.GetServicesRequest{}) + if err != nil { + return []string{}, err + } + return res.Services, nil +} + +func (r *spanReader) GetOperations(ctx context.Context, query spanstore.OperationQueryParameters) ([]spanstore.Operation, error) { + var operations []spanstore.Operation + res, err := r.client.GetOperations(ctx, &api_v2.GetOperationsRequest{ + Service: query.ServiceName, + SpanKind: query.SpanKind, + }) + if err != nil { + return operations, err + } + + for _, operation := range res.Operations { + operations = append(operations, spanstore.Operation{ + Name: operation.Name, + SpanKind: operation.SpanKind, + }) + } + return operations, nil +} + +func (r *spanReader) FindTraces(ctx context.Context, query *spanstore.TraceQueryParameters) ([]*model.Trace, error) { + var traces []*model.Trace + stream, err := r.client.FindTraces(ctx, &api_v2.FindTracesRequest{ + Query: &api_v2.TraceQueryParameters{ + ServiceName: query.ServiceName, + OperationName: query.OperationName, + Tags: query.Tags, + StartTimeMin: query.StartTimeMin, + StartTimeMax: query.StartTimeMax, + DurationMin: query.DurationMin, + DurationMax: query.DurationMax, + SearchDepth: int32(query.NumTraces), + }, + }) + if err != nil { + return traces, err + } + + spanMaps := map[string][]*model.Span{} + for received, err := stream.Recv(); !errors.Is(err, io.EOF); received, err = stream.Recv() { + if err != nil { + return nil, unwrapNotFoundErr(err) + } + for i, span := range received.Spans { + traceID := span.TraceID.String() + if _, ok := spanMaps[traceID]; !ok { + spanMaps[traceID] = make([]*model.Span, 0) + } + spanMaps[traceID] = append(spanMaps[traceID], &received.Spans[i]) + } + } + + for _, spans := range spanMaps { + traces = append(traces, &model.Trace{ + Spans: spans, + }) + } + return traces, nil +} + +func (r *spanReader) FindTraceIDs(ctx context.Context, query *spanstore.TraceQueryParameters) ([]model.TraceID, error) { + panic("not implemented") +} diff --git a/plugin/storage/integration/integration.go b/plugin/storage/integration/integration.go index 506900e8ee3..9323d8ccf6b 100644 --- a/plugin/storage/integration/integration.go +++ b/plugin/storage/integration/integration.go @@ -278,7 +278,7 @@ func (s *StorageIntegration) testGetTrace(t *testing.T) { } t.Run("NotFound error", func(t *testing.T) { - fakeTraceID := model.TraceID{High: 0, Low: 0} + fakeTraceID := model.TraceID{High: 0, Low: 1} trace, err := s.SpanReader.GetTrace(context.Background(), fakeTraceID) assert.Equal(t, spanstore.ErrTraceNotFound, err) assert.Nil(t, trace) @@ -538,3 +538,11 @@ func (s *StorageIntegration) RunAll(t *testing.T) { t.Run("GetThroughput", s.testGetThroughput) t.Run("GetLatestProbability", s.testGetLatestProbability) } + +func (s *StorageIntegration) IntegrationTestSpanstore(t *testing.T) { + t.Run("GetServices", s.testGetServices) + t.Run("GetOperations", s.testGetOperations) + t.Run("GetTrace", s.testGetTrace) + t.Run("GetLargeSpans", s.testGetLargeSpan) + t.Run("FindTraces", s.testFindTraces) +} From 631f0305a4a3a8320ad56a4cd48c0abf9d06cf20 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Wed, 3 Apr 2024 13:51:21 +0700 Subject: [PATCH 02/29] Refactor changed integration functions Signed-off-by: James Ryans --- .../internal/integration/e2e/grpc_test.go | 24 ++++++------------- .../storage/integration/badgerstore_test.go | 2 +- plugin/storage/integration/cassandra_test.go | 2 +- .../storage/integration/elasticsearch_test.go | 4 ++-- .../integration/es_index_cleaner_test.go | 6 ++--- .../integration/es_index_rollover_test.go | 4 ++-- plugin/storage/integration/grpc_test.go | 6 ++--- plugin/storage/integration/integration.go | 5 ++-- plugin/storage/integration/kafka_test.go | 2 +- plugin/storage/integration/memstore_test.go | 2 +- 10 files changed, 24 insertions(+), 33 deletions(-) diff --git a/cmd/jaeger/internal/integration/e2e/grpc_test.go b/cmd/jaeger/internal/integration/e2e/grpc_test.go index c3a6a78008e..5580b8d4937 100644 --- a/cmd/jaeger/internal/integration/e2e/grpc_test.go +++ b/cmd/jaeger/internal/integration/e2e/grpc_test.go @@ -16,6 +16,7 @@ import ( "github.com/jaegertracing/jaeger/pkg/metrics" "github.com/jaegertracing/jaeger/pkg/tenancy" "github.com/jaegertracing/jaeger/plugin/storage" + "github.com/jaegertracing/jaeger/plugin/storage/integration" "github.com/jaegertracing/jaeger/ports" ) @@ -33,7 +34,6 @@ func (s *GRPCStorageIntegration) initialize() error { return err } - s.Refresh = s.refresh s.CleanUp = s.cleanUp return nil } @@ -69,24 +69,14 @@ func (s *GRPCStorageIntegration) startServer() error { return nil } -func (s *GRPCStorageIntegration) refresh() error { - return nil -} - -func (s *GRPCStorageIntegration) cleanUp() error { - if err := s.server.Close(); err != nil { - return err - } - if err := s.storageFactory.Close(); err != nil { - return err - } - return s.initialize() +func (s *GRPCStorageIntegration) cleanUp(t *testing.T) { + require.NoError(t, s.server.Close()) + require.NoError(t, s.storageFactory.Close()) + require.NoError(t, s.initialize()) } func TestGRPCStorage(t *testing.T) { - if os.Getenv("STORAGE") != "grpc" { - t.Skip("Integration test against gRPC skipped; set STORAGE env var to grpc to run this") - } + integration.SkipUnlessEnv(t, "grpc") logger, err := zap.NewDevelopment() require.NoError(t, err) @@ -99,5 +89,5 @@ func TestGRPCStorage(t *testing.T) { t.Cleanup(func() { require.NoError(t, s.e2eCleanUp()) }) - s.IntegrationTestSpanstore(t) + s.RunTestSpanstore(t) } diff --git a/plugin/storage/integration/badgerstore_test.go b/plugin/storage/integration/badgerstore_test.go index e3502a30582..ece7576ceea 100644 --- a/plugin/storage/integration/badgerstore_test.go +++ b/plugin/storage/integration/badgerstore_test.go @@ -67,7 +67,7 @@ func (s *BadgerIntegrationStorage) cleanUp(t *testing.T) { } func TestBadgerStorage(t *testing.T) { - skipUnlessEnv(t, "badger") + SkipUnlessEnv(t, "badger") s := &BadgerIntegrationStorage{} s.initialize(t) s.RunAll(t) diff --git a/plugin/storage/integration/cassandra_test.go b/plugin/storage/integration/cassandra_test.go index 0ba286a87e9..17cf08d7c0e 100644 --- a/plugin/storage/integration/cassandra_test.go +++ b/plugin/storage/integration/cassandra_test.go @@ -109,7 +109,7 @@ func (s *CassandraStorageIntegration) initializeDependencyReaderAndWriter(t *tes } func TestCassandraStorage(t *testing.T) { - skipUnlessEnv(t, "cassandra") + SkipUnlessEnv(t, "cassandra") s := newCassandraStorageIntegration() s.initializeCassandra(t) s.RunAll(t) diff --git a/plugin/storage/integration/elasticsearch_test.go b/plugin/storage/integration/elasticsearch_test.go index e386f802be9..8b700478e11 100644 --- a/plugin/storage/integration/elasticsearch_test.go +++ b/plugin/storage/integration/elasticsearch_test.go @@ -268,7 +268,7 @@ func healthCheck() error { } func testElasticsearchStorage(t *testing.T, allTagsAsFields bool) { - skipUnlessEnv(t, "elasticsearch", "opensearch") + SkipUnlessEnv(t, "elasticsearch", "opensearch") if err := healthCheck(); err != nil { t.Fatal(err) } @@ -289,7 +289,7 @@ func TestElasticsearchStorage_AllTagsAsObjectFields(t *testing.T) { } func TestElasticsearchStorage_IndexTemplates(t *testing.T) { - skipUnlessEnv(t, "elasticsearch", "opensearch") + SkipUnlessEnv(t, "elasticsearch", "opensearch") if err := healthCheck(); err != nil { t.Fatal(err) } diff --git a/plugin/storage/integration/es_index_cleaner_test.go b/plugin/storage/integration/es_index_cleaner_test.go index d500eb17033..fbc63bc94c9 100644 --- a/plugin/storage/integration/es_index_cleaner_test.go +++ b/plugin/storage/integration/es_index_cleaner_test.go @@ -40,7 +40,7 @@ const ( ) func TestIndexCleaner_doNotFailOnEmptyStorage(t *testing.T) { - skipUnlessEnv(t, "elasticsearch", "opensearch") + SkipUnlessEnv(t, "elasticsearch", "opensearch") client, err := createESClient() require.NoError(t, err) _, err = client.DeleteIndex("*").Do(context.Background()) @@ -60,7 +60,7 @@ func TestIndexCleaner_doNotFailOnEmptyStorage(t *testing.T) { } func TestIndexCleaner_doNotFailOnFullStorage(t *testing.T) { - skipUnlessEnv(t, "elasticsearch", "opensearch") + SkipUnlessEnv(t, "elasticsearch", "opensearch") client, err := createESClient() require.NoError(t, err) tests := []struct { @@ -82,7 +82,7 @@ func TestIndexCleaner_doNotFailOnFullStorage(t *testing.T) { } func TestIndexCleaner(t *testing.T) { - skipUnlessEnv(t, "elasticsearch", "opensearch") + SkipUnlessEnv(t, "elasticsearch", "opensearch") client, err := createESClient() require.NoError(t, err) v8Client, err := createESV8Client() diff --git a/plugin/storage/integration/es_index_rollover_test.go b/plugin/storage/integration/es_index_rollover_test.go index 930f1b19533..45d874c4475 100644 --- a/plugin/storage/integration/es_index_rollover_test.go +++ b/plugin/storage/integration/es_index_rollover_test.go @@ -30,7 +30,7 @@ const ( ) func TestIndexRollover_FailIfILMNotPresent(t *testing.T) { - skipUnlessEnv(t, "elasticsearch", "opensearch") + SkipUnlessEnv(t, "elasticsearch", "opensearch") client, err := createESClient() require.NoError(t, err) esVersion, err := getVersion(client) @@ -50,7 +50,7 @@ func TestIndexRollover_FailIfILMNotPresent(t *testing.T) { } func TestIndexRollover_CreateIndicesWithILM(t *testing.T) { - skipUnlessEnv(t, "elasticsearch", "opensearch") + SkipUnlessEnv(t, "elasticsearch", "opensearch") // Test using the default ILM Policy Name, i.e. do not pass the ES_ILM_POLICY_NAME env var to the rollover script. t.Run("DefaultPolicyName", func(t *testing.T) { runCreateIndicesWithILM(t, defaultILMPolicyName) diff --git a/plugin/storage/integration/grpc_test.go b/plugin/storage/integration/grpc_test.go index 0d1328cbeae..b04632b7b60 100644 --- a/plugin/storage/integration/grpc_test.go +++ b/plugin/storage/integration/grpc_test.go @@ -151,7 +151,7 @@ func getPluginFlags(t *testing.T) []string { } func TestGRPCStorage(t *testing.T) { - skipUnlessEnv(t, "grpc") + SkipUnlessEnv(t, "grpc") flags := getPluginFlags(t) if configPath := os.Getenv("PLUGIN_CONFIG_PATH"); configPath == "" { t.Log("PLUGIN_CONFIG_PATH env var not set") @@ -167,7 +167,7 @@ func TestGRPCStorage(t *testing.T) { } func TestGRPCStreamingWriter(t *testing.T) { - skipUnlessEnv(t, "grpc") + SkipUnlessEnv(t, "grpc") flags := getPluginFlags(t) wd, err := os.Getwd() require.NoError(t, err) @@ -183,7 +183,7 @@ func TestGRPCStreamingWriter(t *testing.T) { } func TestGRPCRemoteStorage(t *testing.T) { - skipUnlessEnv(t, "grpc") + SkipUnlessEnv(t, "grpc") flags := []string{ "--grpc-storage.server=localhost:2001", "--grpc-storage.tls.enabled=false", diff --git a/plugin/storage/integration/integration.go b/plugin/storage/integration/integration.go index 9323d8ccf6b..969dd0a0f9f 100644 --- a/plugin/storage/integration/integration.go +++ b/plugin/storage/integration/integration.go @@ -112,7 +112,7 @@ func (s *StorageIntegration) refresh(t *testing.T) { s.Refresh(t) } -func skipUnlessEnv(t *testing.T, storage ...string) { +func SkipUnlessEnv(t *testing.T, storage ...string) { env := os.Getenv("STORAGE") for _, s := range storage { if env == s { @@ -539,7 +539,8 @@ func (s *StorageIntegration) RunAll(t *testing.T) { t.Run("GetLatestProbability", s.testGetLatestProbability) } -func (s *StorageIntegration) IntegrationTestSpanstore(t *testing.T) { +// RunTestSpanstore runs only span related integration tests +func (s *StorageIntegration) RunTestSpanstore(t *testing.T) { t.Run("GetServices", s.testGetServices) t.Run("GetOperations", s.testGetOperations) t.Run("GetTrace", s.testGetTrace) diff --git a/plugin/storage/integration/kafka_test.go b/plugin/storage/integration/kafka_test.go index 598e711833f..d7c719ac89c 100644 --- a/plugin/storage/integration/kafka_test.go +++ b/plugin/storage/integration/kafka_test.go @@ -132,7 +132,7 @@ func (r *ingester) FindTraceIDs(ctx context.Context, query *spanstore.TraceQuery } func TestKafkaStorage(t *testing.T) { - skipUnlessEnv(t, "kafka") + SkipUnlessEnv(t, "kafka") s := &KafkaIntegrationTestSuite{} s.initialize(t) t.Run("GetTrace", s.testGetTrace) diff --git a/plugin/storage/integration/memstore_test.go b/plugin/storage/integration/memstore_test.go index 875781a80ce..305cb5b2fc8 100644 --- a/plugin/storage/integration/memstore_test.go +++ b/plugin/storage/integration/memstore_test.go @@ -47,7 +47,7 @@ func (s *MemStorageIntegrationTestSuite) initialize(_ *testing.T) { } func TestMemoryStorage(t *testing.T) { - skipUnlessEnv(t, "memory") + SkipUnlessEnv(t, "memory") s := &MemStorageIntegrationTestSuite{} s.initialize(t) s.RunAll(t) From 7f2e18e9f71d0fa330435c61cb4be4d786650cd2 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Wed, 3 Apr 2024 13:55:26 +0700 Subject: [PATCH 03/29] Fix should have grpc_test empty refresh function Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/e2e/grpc_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/jaeger/internal/integration/e2e/grpc_test.go b/cmd/jaeger/internal/integration/e2e/grpc_test.go index 5580b8d4937..8fee002899c 100644 --- a/cmd/jaeger/internal/integration/e2e/grpc_test.go +++ b/cmd/jaeger/internal/integration/e2e/grpc_test.go @@ -34,6 +34,7 @@ func (s *GRPCStorageIntegration) initialize() error { return err } + s.Refresh = func(_ *testing.T) {} s.CleanUp = s.cleanUp return nil } From 5ffd7ddfcdd60a6a7539d751b09edb2f644ea0ce Mon Sep 17 00:00:00 2001 From: James Ryans Date: Wed, 3 Apr 2024 13:55:49 +0700 Subject: [PATCH 04/29] Add explanations to the e2e storage integration Signed-off-by: James Ryans --- .../internal/integration/e2e/integration.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cmd/jaeger/internal/integration/e2e/integration.go b/cmd/jaeger/internal/integration/e2e/integration.go index 362a9e48e3b..8c2b549def8 100644 --- a/cmd/jaeger/internal/integration/e2e/integration.go +++ b/cmd/jaeger/internal/integration/e2e/integration.go @@ -30,6 +30,13 @@ var ( _ spanstore.Reader = (*spanReader)(nil) ) +// E2EStorageIntegration holds components for e2e mode of Jaeger-v2 +// storage integration test. The intended usage is as follows: +// - A specific storage implementation declares its own test functions +// (e.g. starts remote-storage). +// - In those functions, instantiates with e2eInitialize() +// and clean up with e2eCleanUp(). +// - Then calls RunTestSpanstore. type E2EStorageIntegration struct { integration.StorageIntegration @@ -37,6 +44,8 @@ type E2EStorageIntegration struct { configCleanup func() } +// e2eInitialize starts the Jaeger-v2 collector with the provided config file, +// it also initialize the SpanWriter and SpanReader below. func (s *E2EStorageIntegration) e2eInitialize() error { factories, err := internal.Components() if err != nil { @@ -58,10 +67,10 @@ func (s *E2EStorageIntegration) e2eInitialize() error { return err } - if s.SpanWriter, err = CreateSpanWriter(); err != nil { + if s.SpanWriter, err = createSpanWriter(); err != nil { return err } - if s.SpanReader, err = CreateSpanReader(); err != nil { + if s.SpanReader, err = createSpanReader(); err != nil { return err } return nil @@ -73,11 +82,12 @@ func (s *E2EStorageIntegration) e2eCleanUp() error { return err } +// SpanWriter utilizes the OTLP exporter to send span data to the Jaeger-v2 receiver type spanWriter struct { testbed.TraceDataSender } -func CreateSpanWriter() (*spanWriter, error) { +func createSpanWriter() (*spanWriter, error) { sender := testbed.NewOTLPTraceDataSender(testbed.DefaultHost, testbed.DefaultOTLPPort) err := sender.Start() if err != nil { @@ -103,11 +113,12 @@ func (w *spanWriter) WriteSpan(ctx context.Context, span *model.Span) error { return w.ConsumeTraces(ctx, td) } +// SpanReader retrieve span data from Jaeger-v2 query with api_v2.QueryServiceClient. type spanReader struct { client api_v2.QueryServiceClient } -func CreateSpanReader() (*spanReader, error) { +func createSpanReader() (*spanReader, error) { opts := []grpc.DialOption{ grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials()), From ad61084e936294a934f4d295bdc49466b389bca8 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Wed, 3 Apr 2024 14:12:19 +0700 Subject: [PATCH 05/29] Refactor hardcoded config file into a struct field Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/e2e/grpc_test.go | 1 + cmd/jaeger/internal/integration/e2e/integration.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/jaeger/internal/integration/e2e/grpc_test.go b/cmd/jaeger/internal/integration/e2e/grpc_test.go index 8fee002899c..6b4fd01cec3 100644 --- a/cmd/jaeger/internal/integration/e2e/grpc_test.go +++ b/cmd/jaeger/internal/integration/e2e/grpc_test.go @@ -85,6 +85,7 @@ func TestGRPCStorage(t *testing.T) { s := &GRPCStorageIntegration{ logger: logger, } + s.ConfigFile = "../../../grpc_config.yaml" require.NoError(t, s.initialize()) require.NoError(t, s.e2eInitialize()) t.Cleanup(func() { diff --git a/cmd/jaeger/internal/integration/e2e/integration.go b/cmd/jaeger/internal/integration/e2e/integration.go index 8c2b549def8..fb288cd79e3 100644 --- a/cmd/jaeger/internal/integration/e2e/integration.go +++ b/cmd/jaeger/internal/integration/e2e/integration.go @@ -39,6 +39,7 @@ var ( // - Then calls RunTestSpanstore. type E2EStorageIntegration struct { integration.StorageIntegration + ConfigFile string runner testbed.OtelcolRunner configCleanup func() @@ -52,7 +53,7 @@ func (s *E2EStorageIntegration) e2eInitialize() error { return err } - config, err := os.ReadFile("../../../grpc_config.yaml") + config, err := os.ReadFile(s.ConfigFile) if err != nil { return err } From 562c85becd44c94fd63466538e5b2581725f0566 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Wed, 3 Apr 2024 14:55:43 +0700 Subject: [PATCH 06/29] Fix CodeQL on SpanReader.FindTraces query.NumTraces cast Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/e2e/integration.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/jaeger/internal/integration/e2e/integration.go b/cmd/jaeger/internal/integration/e2e/integration.go index fb288cd79e3..f72e89be107 100644 --- a/cmd/jaeger/internal/integration/e2e/integration.go +++ b/cmd/jaeger/internal/integration/e2e/integration.go @@ -6,7 +6,9 @@ package e2e import ( "context" "errors" + "fmt" "io" + "math" "os" "strings" "time" @@ -199,6 +201,10 @@ func (r *spanReader) GetOperations(ctx context.Context, query spanstore.Operatio func (r *spanReader) FindTraces(ctx context.Context, query *spanstore.TraceQueryParameters) ([]*model.Trace, error) { var traces []*model.Trace + + if query.NumTraces > math.MaxInt32 { + return traces, fmt.Errorf("NumTraces must not greater than %d", math.MaxInt32) + } stream, err := r.client.FindTraces(ctx, &api_v2.FindTracesRequest{ Query: &api_v2.TraceQueryParameters{ ServiceName: query.ServiceName, From 3558e9233fff169fd75b5ceb63edc892a29ad3af Mon Sep 17 00:00:00 2001 From: James Ryans Date: Thu, 4 Apr 2024 06:14:30 +0700 Subject: [PATCH 07/29] Refactor all initialization and clean up function as a test component Signed-off-by: James Ryans --- .../internal/integration/e2e/grpc_test.go | 37 ++++++------------- .../internal/integration/e2e/integration.go | 35 +++++++----------- 2 files changed, 24 insertions(+), 48 deletions(-) diff --git a/cmd/jaeger/internal/integration/e2e/grpc_test.go b/cmd/jaeger/internal/integration/e2e/grpc_test.go index 6b4fd01cec3..0b1357ef809 100644 --- a/cmd/jaeger/internal/integration/e2e/grpc_test.go +++ b/cmd/jaeger/internal/integration/e2e/grpc_test.go @@ -28,18 +28,14 @@ type GRPCStorageIntegration struct { storageFactory *storage.Factory } -func (s *GRPCStorageIntegration) initialize() error { - err := s.startServer() - if err != nil { - return err - } +func (s *GRPCStorageIntegration) initialize(t *testing.T) { + s.startServer(t) s.Refresh = func(_ *testing.T) {} s.CleanUp = s.cleanUp - return nil } -func (s *GRPCStorageIntegration) startServer() error { +func (s *GRPCStorageIntegration) startServer(t *testing.T) { opts := &app.Options{ GRPCHostPort: ports.PortToHostPort(ports.RemoteStorageGRPC), Tenancy: tenancy.Options{ @@ -49,31 +45,20 @@ func (s *GRPCStorageIntegration) startServer() error { tm := tenancy.NewManager(&opts.Tenancy) var err error s.storageFactory, err = storage.NewFactory(storage.FactoryConfigFromEnvAndCLI(os.Args, os.Stderr)) - if err != nil { - return err - } + require.NoError(t, err) v, _ := config.Viperize(s.storageFactory.AddFlags) s.storageFactory.InitFromViper(v, s.logger) - err = s.storageFactory.Initialize(metrics.NullFactory, s.logger) - if err != nil { - return err - } + require.NoError(t, s.storageFactory.Initialize(metrics.NullFactory, s.logger)) s.server, err = app.NewServer(opts, s.storageFactory, tm, s.logger, healthcheck.New()) - if err != nil { - return err - } - err = s.server.Start() - if err != nil { - return err - } - return nil + require.NoError(t, err) + require.NoError(t, s.server.Start()) } func (s *GRPCStorageIntegration) cleanUp(t *testing.T) { require.NoError(t, s.server.Close()) require.NoError(t, s.storageFactory.Close()) - require.NoError(t, s.initialize()) + s.initialize(t) } func TestGRPCStorage(t *testing.T) { @@ -86,10 +71,10 @@ func TestGRPCStorage(t *testing.T) { logger: logger, } s.ConfigFile = "../../../grpc_config.yaml" - require.NoError(t, s.initialize()) - require.NoError(t, s.e2eInitialize()) + s.initialize(t) + s.e2eInitialize(t) t.Cleanup(func() { - require.NoError(t, s.e2eCleanUp()) + s.e2eCleanUp(t) }) s.RunTestSpanstore(t) } diff --git a/cmd/jaeger/internal/integration/e2e/integration.go b/cmd/jaeger/internal/integration/e2e/integration.go index f72e89be107..d546a160d62 100644 --- a/cmd/jaeger/internal/integration/e2e/integration.go +++ b/cmd/jaeger/internal/integration/e2e/integration.go @@ -11,10 +11,12 @@ import ( "math" "os" "strings" + "testing" "time" jaeger2otlp "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" "github.com/open-telemetry/opentelemetry-collector-contrib/testbed/testbed" + "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" @@ -49,40 +51,29 @@ type E2EStorageIntegration struct { // e2eInitialize starts the Jaeger-v2 collector with the provided config file, // it also initialize the SpanWriter and SpanReader below. -func (s *E2EStorageIntegration) e2eInitialize() error { +func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { factories, err := internal.Components() - if err != nil { - return err - } + require.NoError(t, err) config, err := os.ReadFile(s.ConfigFile) - if err != nil { - return err - } + require.NoError(t, err) config = []byte(strings.Replace(string(config), "./cmd/jaeger/", "../../../", 1)) s.runner = testbed.NewInProcessCollector(factories) s.configCleanup, err = s.runner.PrepareConfig(string(config)) - if err != nil { - return err - } - if err = s.runner.Start(testbed.StartParams{}); err != nil { - return err - } + require.NoError(t, err) + require.NoError(t, s.runner.Start(testbed.StartParams{})) - if s.SpanWriter, err = createSpanWriter(); err != nil { - return err - } - if s.SpanReader, err = createSpanReader(); err != nil { - return err - } - return nil + s.SpanWriter, err = createSpanWriter() + require.NoError(t, err) + s.SpanReader, err = createSpanReader() + require.NoError(t, err) } -func (s *E2EStorageIntegration) e2eCleanUp() error { +func (s *E2EStorageIntegration) e2eCleanUp(t *testing.T) { s.configCleanup() _, err := s.runner.Stop() - return err + require.NoError(t, err) } // SpanWriter utilizes the OTLP exporter to send span data to the Jaeger-v2 receiver From 8ece3545e492379ba3060064f15f378bb5e42ce6 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Thu, 4 Apr 2024 06:20:39 +0700 Subject: [PATCH 08/29] Refactor SpanWriter and SpanReader into separate files Signed-off-by: James Ryans --- .../internal/integration/e2e/integration.go | 181 ------------------ .../internal/integration/e2e/span_reader.go | 155 +++++++++++++++ .../internal/integration/e2e/span_writer.go | 46 +++++ 3 files changed, 201 insertions(+), 181 deletions(-) create mode 100644 cmd/jaeger/internal/integration/e2e/span_reader.go create mode 100644 cmd/jaeger/internal/integration/e2e/span_writer.go diff --git a/cmd/jaeger/internal/integration/e2e/integration.go b/cmd/jaeger/internal/integration/e2e/integration.go index d546a160d62..8bf2ddf8d26 100644 --- a/cmd/jaeger/internal/integration/e2e/integration.go +++ b/cmd/jaeger/internal/integration/e2e/integration.go @@ -4,34 +4,15 @@ package e2e import ( - "context" - "errors" - "fmt" - "io" - "math" "os" "strings" "testing" - "time" - jaeger2otlp "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" "github.com/open-telemetry/opentelemetry-collector-contrib/testbed/testbed" "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" "github.com/jaegertracing/jaeger/cmd/jaeger/internal" - "github.com/jaegertracing/jaeger/model" "github.com/jaegertracing/jaeger/plugin/storage/integration" - "github.com/jaegertracing/jaeger/ports" - "github.com/jaegertracing/jaeger/proto-gen/api_v2" - "github.com/jaegertracing/jaeger/storage/spanstore" -) - -var ( - _ spanstore.Writer = (*spanWriter)(nil) - _ spanstore.Reader = (*spanReader)(nil) ) // E2EStorageIntegration holds components for e2e mode of Jaeger-v2 @@ -75,165 +56,3 @@ func (s *E2EStorageIntegration) e2eCleanUp(t *testing.T) { _, err := s.runner.Stop() require.NoError(t, err) } - -// SpanWriter utilizes the OTLP exporter to send span data to the Jaeger-v2 receiver -type spanWriter struct { - testbed.TraceDataSender -} - -func createSpanWriter() (*spanWriter, error) { - sender := testbed.NewOTLPTraceDataSender(testbed.DefaultHost, testbed.DefaultOTLPPort) - err := sender.Start() - if err != nil { - return nil, err - } - - return &spanWriter{ - TraceDataSender: sender, - }, nil -} - -func (w *spanWriter) WriteSpan(ctx context.Context, span *model.Span) error { - td, err := jaeger2otlp.ProtoToTraces([]*model.Batch{ - { - Spans: []*model.Span{span}, - Process: span.Process, - }, - }) - if err != nil { - return err - } - - return w.ConsumeTraces(ctx, td) -} - -// SpanReader retrieve span data from Jaeger-v2 query with api_v2.QueryServiceClient. -type spanReader struct { - client api_v2.QueryServiceClient -} - -func createSpanReader() (*spanReader, error) { - opts := []grpc.DialOption{ - grpc.WithBlock(), - grpc.WithTransportCredentials(insecure.NewCredentials()), - } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - cc, err := grpc.DialContext(ctx, ports.PortToHostPort(ports.QueryGRPC), opts...) - if err != nil { - return nil, err - } - - return &spanReader{ - client: api_v2.NewQueryServiceClient(cc), - }, nil -} - -func unwrapNotFoundErr(err error) error { - if s, _ := status.FromError(err); s != nil { - if strings.Contains(s.Message(), spanstore.ErrTraceNotFound.Error()) { - return spanstore.ErrTraceNotFound - } - } - return err -} - -func (r *spanReader) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { - stream, err := r.client.GetTrace(ctx, &api_v2.GetTraceRequest{ - TraceID: traceID, - }) - if err != nil { - return nil, unwrapNotFoundErr(err) - } - - var spans []*model.Span - for received, err := stream.Recv(); !errors.Is(err, io.EOF); received, err = stream.Recv() { - if err != nil { - return nil, unwrapNotFoundErr(err) - } - for i := range received.Spans { - spans = append(spans, &received.Spans[i]) - } - } - - return &model.Trace{ - Spans: spans, - }, nil -} - -func (r *spanReader) GetServices(ctx context.Context) ([]string, error) { - res, err := r.client.GetServices(ctx, &api_v2.GetServicesRequest{}) - if err != nil { - return []string{}, err - } - return res.Services, nil -} - -func (r *spanReader) GetOperations(ctx context.Context, query spanstore.OperationQueryParameters) ([]spanstore.Operation, error) { - var operations []spanstore.Operation - res, err := r.client.GetOperations(ctx, &api_v2.GetOperationsRequest{ - Service: query.ServiceName, - SpanKind: query.SpanKind, - }) - if err != nil { - return operations, err - } - - for _, operation := range res.Operations { - operations = append(operations, spanstore.Operation{ - Name: operation.Name, - SpanKind: operation.SpanKind, - }) - } - return operations, nil -} - -func (r *spanReader) FindTraces(ctx context.Context, query *spanstore.TraceQueryParameters) ([]*model.Trace, error) { - var traces []*model.Trace - - if query.NumTraces > math.MaxInt32 { - return traces, fmt.Errorf("NumTraces must not greater than %d", math.MaxInt32) - } - stream, err := r.client.FindTraces(ctx, &api_v2.FindTracesRequest{ - Query: &api_v2.TraceQueryParameters{ - ServiceName: query.ServiceName, - OperationName: query.OperationName, - Tags: query.Tags, - StartTimeMin: query.StartTimeMin, - StartTimeMax: query.StartTimeMax, - DurationMin: query.DurationMin, - DurationMax: query.DurationMax, - SearchDepth: int32(query.NumTraces), - }, - }) - if err != nil { - return traces, err - } - - spanMaps := map[string][]*model.Span{} - for received, err := stream.Recv(); !errors.Is(err, io.EOF); received, err = stream.Recv() { - if err != nil { - return nil, unwrapNotFoundErr(err) - } - for i, span := range received.Spans { - traceID := span.TraceID.String() - if _, ok := spanMaps[traceID]; !ok { - spanMaps[traceID] = make([]*model.Span, 0) - } - spanMaps[traceID] = append(spanMaps[traceID], &received.Spans[i]) - } - } - - for _, spans := range spanMaps { - traces = append(traces, &model.Trace{ - Spans: spans, - }) - } - return traces, nil -} - -func (r *spanReader) FindTraceIDs(ctx context.Context, query *spanstore.TraceQueryParameters) ([]model.TraceID, error) { - panic("not implemented") -} diff --git a/cmd/jaeger/internal/integration/e2e/span_reader.go b/cmd/jaeger/internal/integration/e2e/span_reader.go new file mode 100644 index 00000000000..308c06388d7 --- /dev/null +++ b/cmd/jaeger/internal/integration/e2e/span_reader.go @@ -0,0 +1,155 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "context" + "errors" + "fmt" + "io" + "math" + "strings" + "time" + + "github.com/jaegertracing/jaeger/model" + "github.com/jaegertracing/jaeger/ports" + "github.com/jaegertracing/jaeger/proto-gen/api_v2" + "github.com/jaegertracing/jaeger/storage/spanstore" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" +) + +var _ spanstore.Reader = (*spanReader)(nil) + +// SpanReader retrieve span data from Jaeger-v2 query with api_v2.QueryServiceClient. +type spanReader struct { + client api_v2.QueryServiceClient +} + +func createSpanReader() (*spanReader, error) { + opts := []grpc.DialOption{ + grpc.WithBlock(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + cc, err := grpc.DialContext(ctx, ports.PortToHostPort(ports.QueryGRPC), opts...) + if err != nil { + return nil, err + } + + return &spanReader{ + client: api_v2.NewQueryServiceClient(cc), + }, nil +} + +func unwrapNotFoundErr(err error) error { + if s, _ := status.FromError(err); s != nil { + if strings.Contains(s.Message(), spanstore.ErrTraceNotFound.Error()) { + return spanstore.ErrTraceNotFound + } + } + return err +} + +func (r *spanReader) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { + stream, err := r.client.GetTrace(ctx, &api_v2.GetTraceRequest{ + TraceID: traceID, + }) + if err != nil { + return nil, unwrapNotFoundErr(err) + } + + var spans []*model.Span + for received, err := stream.Recv(); !errors.Is(err, io.EOF); received, err = stream.Recv() { + if err != nil { + return nil, unwrapNotFoundErr(err) + } + for i := range received.Spans { + spans = append(spans, &received.Spans[i]) + } + } + + return &model.Trace{ + Spans: spans, + }, nil +} + +func (r *spanReader) GetServices(ctx context.Context) ([]string, error) { + res, err := r.client.GetServices(ctx, &api_v2.GetServicesRequest{}) + if err != nil { + return []string{}, err + } + return res.Services, nil +} + +func (r *spanReader) GetOperations(ctx context.Context, query spanstore.OperationQueryParameters) ([]spanstore.Operation, error) { + var operations []spanstore.Operation + res, err := r.client.GetOperations(ctx, &api_v2.GetOperationsRequest{ + Service: query.ServiceName, + SpanKind: query.SpanKind, + }) + if err != nil { + return operations, err + } + + for _, operation := range res.Operations { + operations = append(operations, spanstore.Operation{ + Name: operation.Name, + SpanKind: operation.SpanKind, + }) + } + return operations, nil +} + +func (r *spanReader) FindTraces(ctx context.Context, query *spanstore.TraceQueryParameters) ([]*model.Trace, error) { + var traces []*model.Trace + + if query.NumTraces > math.MaxInt32 { + return traces, fmt.Errorf("NumTraces must not greater than %d", math.MaxInt32) + } + stream, err := r.client.FindTraces(ctx, &api_v2.FindTracesRequest{ + Query: &api_v2.TraceQueryParameters{ + ServiceName: query.ServiceName, + OperationName: query.OperationName, + Tags: query.Tags, + StartTimeMin: query.StartTimeMin, + StartTimeMax: query.StartTimeMax, + DurationMin: query.DurationMin, + DurationMax: query.DurationMax, + SearchDepth: int32(query.NumTraces), + }, + }) + if err != nil { + return traces, err + } + + spanMaps := map[string][]*model.Span{} + for received, err := stream.Recv(); !errors.Is(err, io.EOF); received, err = stream.Recv() { + if err != nil { + return nil, unwrapNotFoundErr(err) + } + for i, span := range received.Spans { + traceID := span.TraceID.String() + if _, ok := spanMaps[traceID]; !ok { + spanMaps[traceID] = make([]*model.Span, 0) + } + spanMaps[traceID] = append(spanMaps[traceID], &received.Spans[i]) + } + } + + for _, spans := range spanMaps { + traces = append(traces, &model.Trace{ + Spans: spans, + }) + } + return traces, nil +} + +func (r *spanReader) FindTraceIDs(ctx context.Context, query *spanstore.TraceQueryParameters) ([]model.TraceID, error) { + panic("not implemented") +} diff --git a/cmd/jaeger/internal/integration/e2e/span_writer.go b/cmd/jaeger/internal/integration/e2e/span_writer.go new file mode 100644 index 00000000000..53b25097713 --- /dev/null +++ b/cmd/jaeger/internal/integration/e2e/span_writer.go @@ -0,0 +1,46 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package e2e + +import ( + "context" + + "github.com/jaegertracing/jaeger/model" + "github.com/jaegertracing/jaeger/storage/spanstore" + jaeger2otlp "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" + "github.com/open-telemetry/opentelemetry-collector-contrib/testbed/testbed" +) + +var _ spanstore.Writer = (*spanWriter)(nil) + +// SpanWriter utilizes the OTLP exporter to send span data to the Jaeger-v2 receiver +type spanWriter struct { + testbed.TraceDataSender +} + +func createSpanWriter() (*spanWriter, error) { + sender := testbed.NewOTLPTraceDataSender(testbed.DefaultHost, testbed.DefaultOTLPPort) + err := sender.Start() + if err != nil { + return nil, err + } + + return &spanWriter{ + TraceDataSender: sender, + }, nil +} + +func (w *spanWriter) WriteSpan(ctx context.Context, span *model.Span) error { + td, err := jaeger2otlp.ProtoToTraces([]*model.Batch{ + { + Spans: []*model.Span{span}, + Process: span.Process, + }, + }) + if err != nil { + return err + } + + return w.ConsumeTraces(ctx, td) +} From 1127445b6cd1789c9325a3620f0f8eeee1908a77 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Thu, 4 Apr 2024 06:26:29 +0700 Subject: [PATCH 09/29] Refactor RunSpanStoreTests and invoke from RunAll Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/e2e/grpc_test.go | 2 +- plugin/storage/integration/integration.go | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cmd/jaeger/internal/integration/e2e/grpc_test.go b/cmd/jaeger/internal/integration/e2e/grpc_test.go index 0b1357ef809..d66234dd9ed 100644 --- a/cmd/jaeger/internal/integration/e2e/grpc_test.go +++ b/cmd/jaeger/internal/integration/e2e/grpc_test.go @@ -76,5 +76,5 @@ func TestGRPCStorage(t *testing.T) { t.Cleanup(func() { s.e2eCleanUp(t) }) - s.RunTestSpanstore(t) + s.RunSpanStoreTests(t) } diff --git a/plugin/storage/integration/integration.go b/plugin/storage/integration/integration.go index 969dd0a0f9f..b1483e49633 100644 --- a/plugin/storage/integration/integration.go +++ b/plugin/storage/integration/integration.go @@ -528,19 +528,15 @@ func (s *StorageIntegration) insertThroughput(t *testing.T) { // RunAll runs all integration tests func (s *StorageIntegration) RunAll(t *testing.T) { - t.Run("GetServices", s.testGetServices) + s.RunSpanStoreTests(t) t.Run("ArchiveTrace", s.testArchiveTrace) - t.Run("GetOperations", s.testGetOperations) - t.Run("GetTrace", s.testGetTrace) - t.Run("GetLargeSpans", s.testGetLargeSpan) - t.Run("FindTraces", s.testFindTraces) t.Run("GetDependencies", s.testGetDependencies) t.Run("GetThroughput", s.testGetThroughput) t.Run("GetLatestProbability", s.testGetLatestProbability) } // RunTestSpanstore runs only span related integration tests -func (s *StorageIntegration) RunTestSpanstore(t *testing.T) { +func (s *StorageIntegration) RunSpanStoreTests(t *testing.T) { t.Run("GetServices", s.testGetServices) t.Run("GetOperations", s.testGetOperations) t.Run("GetTrace", s.testGetTrace) From b00b2d2c6aabf168dbcd72666d80bf1c985cd411 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Thu, 4 Apr 2024 22:02:43 +0700 Subject: [PATCH 10/29] Refactor extract grpc server into a single file Signed-off-by: James Ryans --- .../internal/integration/e2e/grpc_test.go | 44 ++---------- plugin/storage/integration/grpc_server.go | 67 +++++++++++++++++ plugin/storage/integration/grpc_test.go | 71 +++---------------- 3 files changed, 82 insertions(+), 100 deletions(-) create mode 100644 plugin/storage/integration/grpc_server.go diff --git a/cmd/jaeger/internal/integration/e2e/grpc_test.go b/cmd/jaeger/internal/integration/e2e/grpc_test.go index d66234dd9ed..81be731c321 100644 --- a/cmd/jaeger/internal/integration/e2e/grpc_test.go +++ b/cmd/jaeger/internal/integration/e2e/grpc_test.go @@ -4,71 +4,41 @@ package e2e import ( - "os" "testing" "github.com/stretchr/testify/require" - "go.uber.org/zap" - "github.com/jaegertracing/jaeger/cmd/remote-storage/app" - "github.com/jaegertracing/jaeger/pkg/config" - "github.com/jaegertracing/jaeger/pkg/healthcheck" - "github.com/jaegertracing/jaeger/pkg/metrics" - "github.com/jaegertracing/jaeger/pkg/tenancy" - "github.com/jaegertracing/jaeger/plugin/storage" "github.com/jaegertracing/jaeger/plugin/storage/integration" - "github.com/jaegertracing/jaeger/ports" ) type GRPCStorageIntegration struct { E2EStorageIntegration - logger *zap.Logger - server *app.Server - storageFactory *storage.Factory + server *integration.GRPCServer } func (s *GRPCStorageIntegration) initialize(t *testing.T) { - s.startServer(t) - - s.Refresh = func(_ *testing.T) {} - s.CleanUp = s.cleanUp -} - -func (s *GRPCStorageIntegration) startServer(t *testing.T) { - opts := &app.Options{ - GRPCHostPort: ports.PortToHostPort(ports.RemoteStorageGRPC), - Tenancy: tenancy.Options{ - Enabled: false, - }, - } - tm := tenancy.NewManager(&opts.Tenancy) var err error - s.storageFactory, err = storage.NewFactory(storage.FactoryConfigFromEnvAndCLI(os.Args, os.Stderr)) - require.NoError(t, err) - v, _ := config.Viperize(s.storageFactory.AddFlags) - s.storageFactory.InitFromViper(v, s.logger) - require.NoError(t, s.storageFactory.Initialize(metrics.NullFactory, s.logger)) - - s.server, err = app.NewServer(opts, s.storageFactory, tm, s.logger, healthcheck.New()) + s.server, err = integration.NewGRPCServer() require.NoError(t, err) require.NoError(t, s.server.Start()) + + s.Refresh = func(_ *testing.T) {} + s.CleanUp = s.cleanUp } func (s *GRPCStorageIntegration) cleanUp(t *testing.T) { require.NoError(t, s.server.Close()) - require.NoError(t, s.storageFactory.Close()) s.initialize(t) } func TestGRPCStorage(t *testing.T) { integration.SkipUnlessEnv(t, "grpc") - logger, err := zap.NewDevelopment() + server, err := integration.NewGRPCServer() require.NoError(t, err) - s := &GRPCStorageIntegration{ - logger: logger, + server: server, } s.ConfigFile = "../../../grpc_config.yaml" s.initialize(t) diff --git a/plugin/storage/integration/grpc_server.go b/plugin/storage/integration/grpc_server.go new file mode 100644 index 00000000000..192b97f8355 --- /dev/null +++ b/plugin/storage/integration/grpc_server.go @@ -0,0 +1,67 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package integration + +import ( + "net" + "sync" + + googleGRPC "google.golang.org/grpc" + + grpcMemory "github.com/jaegertracing/jaeger/plugin/storage/grpc/memory" + "github.com/jaegertracing/jaeger/plugin/storage/grpc/shared" + "github.com/jaegertracing/jaeger/plugin/storage/memory" + "github.com/jaegertracing/jaeger/ports" +) + +type GRPCServer struct { + errChan chan error + server *googleGRPC.Server + wg sync.WaitGroup +} + +func NewGRPCServer() (*GRPCServer, error) { + return &GRPCServer{errChan: make(chan error, 1)}, nil +} + +func (s *GRPCServer) Start() error { + memStorePlugin := grpcMemory.NewStoragePlugin(memory.NewStore(), memory.NewStore()) + + s.server = googleGRPC.NewServer() + queryPlugin := shared.StorageGRPCPlugin{ + Impl: memStorePlugin, + ArchiveImpl: memStorePlugin, + } + + if err := queryPlugin.RegisterHandlers(s.server); err != nil { + return err + } + + listener, err := net.Listen("tcp", ports.PortToHostPort(ports.RemoteStorageGRPC)) + if err != nil { + return err + } + s.wg.Add(1) + go func() { + defer s.wg.Done() + if err = s.server.Serve(listener); err != nil { + select { + case s.errChan <- err: + default: + } + } + }() + return nil +} + +func (s *GRPCServer) Close() error { + s.server.GracefulStop() + s.wg.Wait() + select { + case err := <-s.errChan: + return err + default: + } + return nil +} diff --git a/plugin/storage/integration/grpc_test.go b/plugin/storage/integration/grpc_test.go index b04632b7b60..b0d9b33d1e5 100644 --- a/plugin/storage/integration/grpc_test.go +++ b/plugin/storage/integration/grpc_test.go @@ -16,23 +16,17 @@ package integration import ( - "net" "os" "path" - "sync" "testing" "github.com/stretchr/testify/require" "go.uber.org/zap" - googleGRPC "google.golang.org/grpc" "github.com/jaegertracing/jaeger/pkg/config" "github.com/jaegertracing/jaeger/pkg/metrics" "github.com/jaegertracing/jaeger/pkg/testutils" "github.com/jaegertracing/jaeger/plugin/storage/grpc" - grpcMemory "github.com/jaegertracing/jaeger/plugin/storage/grpc/memory" - "github.com/jaegertracing/jaeger/plugin/storage/grpc/shared" - "github.com/jaegertracing/jaeger/plugin/storage/memory" ) const ( @@ -40,70 +34,19 @@ const ( streamingPluginConfigPath = "fixtures/grpc_plugin_conf.yaml" ) -type gRPCServer struct { - errChan chan error - server *googleGRPC.Server - wg sync.WaitGroup -} - -func newgRPCServer() (*gRPCServer, error) { - return &gRPCServer{errChan: make(chan error, 1)}, nil -} - -func (s *gRPCServer) Restart() error { - // stop the server if one already exists - if s.server != nil { - s.server.GracefulStop() - s.wg.Wait() - select { - case err := <-s.errChan: - return err - default: - } - } - - memStorePlugin := grpcMemory.NewStoragePlugin(memory.NewStore(), memory.NewStore()) - - s.server = googleGRPC.NewServer() - queryPlugin := shared.StorageGRPCPlugin{ - Impl: memStorePlugin, - ArchiveImpl: memStorePlugin, - } - - if err := queryPlugin.RegisterHandlers(s.server); err != nil { - return err - } - - listener, err := net.Listen("tcp", "localhost:2001") - if err != nil { - return err - } - s.wg.Add(1) - go func() { - defer s.wg.Done() - if err = s.server.Serve(listener); err != nil { - select { - case s.errChan <- err: - default: - } - } - }() - return nil -} - type GRPCStorageIntegrationTestSuite struct { StorageIntegration logger *zap.Logger flags []string factory *grpc.Factory - server *gRPCServer + server *GRPCServer } func (s *GRPCStorageIntegrationTestSuite) initialize(t *testing.T) { s.logger, _ = testutils.NewLogger() if s.server != nil { - err := s.server.Restart() + err := s.server.Start() require.NoError(t, err) } @@ -132,8 +75,10 @@ func (s *GRPCStorageIntegrationTestSuite) initialize(t *testing.T) { } func (s *GRPCStorageIntegrationTestSuite) cleanUp(t *testing.T) { - err := s.factory.Close() - require.NoError(t, err) + require.NoError(t, s.factory.Close()) + if s.server != nil { + require.NoError(t, s.server.Close()) + } s.initialize(t) } @@ -185,10 +130,10 @@ func TestGRPCStreamingWriter(t *testing.T) { func TestGRPCRemoteStorage(t *testing.T) { SkipUnlessEnv(t, "grpc") flags := []string{ - "--grpc-storage.server=localhost:2001", + "--grpc-storage.server=localhost:17271", "--grpc-storage.tls.enabled=false", } - server, err := newgRPCServer() + server, err := NewGRPCServer() require.NoError(t, err) s := &GRPCStorageIntegrationTestSuite{ From 53226e2488258dce7bb801de9b423775f51c74ab Mon Sep 17 00:00:00 2001 From: James Ryans Date: Thu, 4 Apr 2024 22:59:19 +0700 Subject: [PATCH 11/29] Refactor replace testbed test with new test and change README Signed-off-by: James Ryans --- .codecov.yml | 1 - .github/workflows/ci-grpc.yml | 2 +- Makefile | 6 +- cmd/jaeger/internal/integration/README.md | 43 ++- .../integration/datareceivers/.nocover | 1 - .../datareceivers/jaegerstorage.go | 97 ------ .../internal/integration/e2e/grpc_test.go | 50 --- .../internal/integration/e2e/integration.go | 58 ---- .../fixtures/generated_pict_pairs_spans.txt | 307 ------------------ .../fixtures/generated_pict_pairs_traces.txt | 33 -- .../integration/fixtures/grpc_config.yaml | 23 -- .../integration/fixtures/pict_input_spans.txt | 14 - .../fixtures/pict_input_traces.txt | 3 - cmd/jaeger/internal/integration/grpc_test.go | 42 ++- .../integration/integration-diagram.png | Bin 307183 -> 0 bytes .../internal/integration/integration.go | 132 ++------ .../integration/{e2e => }/span_reader.go | 2 +- .../integration/{e2e => }/span_writer.go | 2 +- 18 files changed, 102 insertions(+), 714 deletions(-) delete mode 100644 cmd/jaeger/internal/integration/datareceivers/.nocover delete mode 100644 cmd/jaeger/internal/integration/datareceivers/jaegerstorage.go delete mode 100644 cmd/jaeger/internal/integration/e2e/grpc_test.go delete mode 100644 cmd/jaeger/internal/integration/e2e/integration.go delete mode 100644 cmd/jaeger/internal/integration/fixtures/generated_pict_pairs_spans.txt delete mode 100644 cmd/jaeger/internal/integration/fixtures/generated_pict_pairs_traces.txt delete mode 100644 cmd/jaeger/internal/integration/fixtures/grpc_config.yaml delete mode 100644 cmd/jaeger/internal/integration/fixtures/pict_input_spans.txt delete mode 100644 cmd/jaeger/internal/integration/fixtures/pict_input_traces.txt delete mode 100644 cmd/jaeger/internal/integration/integration-diagram.png rename cmd/jaeger/internal/integration/{e2e => }/span_reader.go (99%) rename cmd/jaeger/internal/integration/{e2e => }/span_writer.go (98%) diff --git a/.codecov.yml b/.codecov.yml index 541321719e2..99f65b0154a 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -11,7 +11,6 @@ ignore: - "thrift-gen/*/*" - "**/thrift-0.9.2/*" - "**/main.go" - - "cmd/jaeger/internal/integration/datareceivers" - "examples/hotrod" coverage: diff --git a/.github/workflows/ci-grpc.yml b/.github/workflows/ci-grpc.yml index 68165182b15..e523f88f618 100644 --- a/.github/workflows/ci-grpc.yml +++ b/.github/workflows/ci-grpc.yml @@ -42,7 +42,7 @@ jobs: v2) STORAGE=grpc \ SPAN_STORAGE_TYPE=memory \ - make jaeger-storage-integration-test + make jaeger-v2-storage-integration-test ;; esac diff --git a/Makefile b/Makefile index b38c4b749ce..89e31d22269 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ SHELL := /bin/bash JAEGER_IMPORT_PATH = github.com/jaegertracing/jaeger STORAGE_PKGS = ./plugin/storage/integration/... -JAEGER_V2_STORAGE_PKGS = ./cmd/jaeger/internal/integration/e2e +JAEGER_V2_STORAGE_PKGS = ./cmd/jaeger/internal/integration # These DOCKER_xxx vars are used when building Docker images. DOCKER_NAMESPACE?=jaegertracing @@ -118,8 +118,8 @@ all-in-one-integration-test: # The integration tests are filtered by STORAGE env, # currently the available STORAGE variable is: # - grpc -.PHONY: jaeger-storage-integration-test -jaeger-storage-integration-test: +.PHONY: jaeger-v2-storage-integration-test +jaeger-v2-storage-integration-test: # Expire tests results for jaeger storage integration tests since the environment might change # even though the code remains the same. go clean -testcache diff --git a/cmd/jaeger/internal/integration/README.md b/cmd/jaeger/internal/integration/README.md index e530ae36ce5..b124984b607 100644 --- a/cmd/jaeger/internal/integration/README.md +++ b/cmd/jaeger/internal/integration/README.md @@ -1,27 +1,42 @@ # Integration -Jaeger v2 integration tests are built on top of [OTEL Testbed module](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/testbed). OTEL Testbed provide comprehensive tools for conducting end-to-end tests for the OTEL Collector, such as reproducible short-term benchmarks, correctness tests, long-running stability tests and maximum load stress tests. However, we only utilize the correctness tests from testbed, it generates and sends every combinatorial trace attributes and matches every single of them with the received traces from another end. To learn more about OTEL Testbed, please refer to the their [README](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/testbed/README.md). +The Jaeger v2 integration test is an extension of the existing `integration.StorageIntegration` designed to test the Jaeger-v2 OtelCol binary; currently, it only tests the span store. The existing tests at `plugin/storage/integration` (also called "unit mode") test by writing and reading span data directly to the storage API. In contrast, these tests (or "e2e mode") read and write span data through the RPC client to the Jaeger-v2 OtelCol binary. E2E mode tests read from the jaeger_query extension and write to the receiver in OTLP formats. For details, see the [Architecture](#architecture) section below. ## Architecture -Here's the architecture to test the OpenTelemetry Collector pipeline from end-to-end with the designated storage backends. -![integration diagram](integration-diagram.png) +```mermaid +flowchart LR + Test -->|writeSpan| SpanWriter + SpanWriter --> RPCW[RPC_client] + RPCW --> Receiver + Receiver --> Exporter + Exporter --> B(StorageBackend) + Test -->|readSpan| SpanReader + SpanReader --> RPCR[RPC_client] + RPCR --> jaeger_query + jaeger_query --> B -Testbed components: -| Component | Description | -|-----------|-------------| -| **LoadGenerator** | Encapsulates DataProvider and DataSender in order to generate and send data. | -| Golden DataProvider | Generates traces from the "Golden" dataset generated using pairwise combinatorial testing techniques. Testbed example uses [PICT](https://github.com/microsoft/pict/) to generate the test data, e.g. [testdata](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/internal/coreinternal/goldendataset/testdata). | -| OTLP Trace DataSender | With the generated traces from DataProvider, the DataSender sends traces to OTLP receiver in the collector instance. | -| **Mockbackend** | Encapsulates DataReceiver and provides consume functionality. | -| DataReceiver | A custom DataReceiver that will host a Jaeger storage extension to retrieve traces from the database by pulling them using our artificial Jaeger storage receiver. | -| Consumer | Consumer does not actually a thing in MockBackend but only to make the diagram intuitive, the traces received from our artificial receiver will be stored inside MockBackend. | -| **Correctness Test Validator** | Checks if the traces received from MockBackend are all matches with the generated traces from DataProvider. | + subgraph Integration Test Executable + Test + SpanWriter + SpanReader + RPCW + RPCR + end + + subgraph jaeger-v2 + Receiver + Exporter + jaeger_query + end +``` ## gRPC Integration Test To conduct the tests, run the following command: ``` -scripts/grpc-integration-test.sh +STORAGE=grpc \ + SPAN_STORAGE_TYPE=memory \ + make jaeger-v2-storage-integration-test ``` diff --git a/cmd/jaeger/internal/integration/datareceivers/.nocover b/cmd/jaeger/internal/integration/datareceivers/.nocover deleted file mode 100644 index 8a76746bfe7..00000000000 --- a/cmd/jaeger/internal/integration/datareceivers/.nocover +++ /dev/null @@ -1 +0,0 @@ -A custom testbed data receiver for integration testing purpose \ No newline at end of file diff --git a/cmd/jaeger/internal/integration/datareceivers/jaegerstorage.go b/cmd/jaeger/internal/integration/datareceivers/jaegerstorage.go deleted file mode 100644 index 688d0866373..00000000000 --- a/cmd/jaeger/internal/integration/datareceivers/jaegerstorage.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2024 The Jaeger Authors. -// SPDX-License-Identifier: Apache-2.0 - -package datareceivers - -import ( - "context" - "fmt" - "time" - - "github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/storagetest" - "github.com/open-telemetry/opentelemetry-collector-contrib/testbed/testbed" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/extension" - "go.opentelemetry.io/collector/receiver" - - "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" - "github.com/jaegertracing/jaeger/cmd/jaeger/internal/integration/receivers/storagereceiver" -) - -type jaegerStorageDataReceiver struct { - TelemetrySettings component.TelemetrySettings - TraceStorage string - StorageConfig *jaegerstorage.Config - host *storagetest.StorageHost - receiver receiver.Traces -} - -func NewJaegerStorageDataReceiver( - telemetrySettings component.TelemetrySettings, - traceStorage string, - storageConfig *jaegerstorage.Config, -) testbed.DataReceiver { - return &jaegerStorageDataReceiver{ - TelemetrySettings: telemetrySettings, - TraceStorage: traceStorage, - StorageConfig: storageConfig, - } -} - -func (dr *jaegerStorageDataReceiver) Start(tc consumer.Traces, _ consumer.Metrics, _ consumer.Logs) error { - ctx := context.Background() - - extSet := extension.CreateSettings{ - ID: jaegerstorage.ID, - TelemetrySettings: dr.TelemetrySettings, - } - extFactory := jaegerstorage.NewFactory() - ext, err := extFactory.CreateExtension(ctx, extSet, dr.StorageConfig) - if err != nil { - return err - } - - rcvSet := receiver.CreateSettings{ - ID: storagereceiver.ID, - TelemetrySettings: dr.TelemetrySettings, - } - rcvFactory := storagereceiver.NewFactory() - rcvCfg := rcvFactory.CreateDefaultConfig().(*storagereceiver.Config) - rcvCfg.TraceStorage = dr.TraceStorage - rcvCfg.PullInterval = 100 * time.Millisecond - rcv, err := rcvFactory.CreateTracesReceiver(ctx, rcvSet, rcvCfg, tc) - if err != nil { - return err - } - dr.receiver = rcv - - dr.host = storagetest.NewStorageHost() - dr.host.WithExtension(jaegerstorage.ID, ext) - - err = dr.host.GetExtensions()[jaegerstorage.ID].Start(ctx, dr.host) - if err != nil { - return err - } - return dr.receiver.Start(ctx, dr.host) -} - -func (dr *jaegerStorageDataReceiver) Stop() error { - ctx := context.Background() - err := dr.receiver.Shutdown(ctx) - if err != nil { - return err - } - return dr.host.GetExtensions()[jaegerstorage.ID].Shutdown(ctx) -} - -func (dr *jaegerStorageDataReceiver) GenConfigYAMLStr() string { - return fmt.Sprintf(` - jaeger_storage_receiver: - trace_storage: %s -`, dr.TraceStorage) -} - -func (dr *jaegerStorageDataReceiver) ProtocolName() string { - return "jaeger_storage_receiver" -} diff --git a/cmd/jaeger/internal/integration/e2e/grpc_test.go b/cmd/jaeger/internal/integration/e2e/grpc_test.go deleted file mode 100644 index 81be731c321..00000000000 --- a/cmd/jaeger/internal/integration/e2e/grpc_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2024 The Jaeger Authors. -// SPDX-License-Identifier: Apache-2.0 - -package e2e - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/jaegertracing/jaeger/plugin/storage/integration" -) - -type GRPCStorageIntegration struct { - E2EStorageIntegration - - server *integration.GRPCServer -} - -func (s *GRPCStorageIntegration) initialize(t *testing.T) { - var err error - s.server, err = integration.NewGRPCServer() - require.NoError(t, err) - require.NoError(t, s.server.Start()) - - s.Refresh = func(_ *testing.T) {} - s.CleanUp = s.cleanUp -} - -func (s *GRPCStorageIntegration) cleanUp(t *testing.T) { - require.NoError(t, s.server.Close()) - s.initialize(t) -} - -func TestGRPCStorage(t *testing.T) { - integration.SkipUnlessEnv(t, "grpc") - - server, err := integration.NewGRPCServer() - require.NoError(t, err) - s := &GRPCStorageIntegration{ - server: server, - } - s.ConfigFile = "../../../grpc_config.yaml" - s.initialize(t) - s.e2eInitialize(t) - t.Cleanup(func() { - s.e2eCleanUp(t) - }) - s.RunSpanStoreTests(t) -} diff --git a/cmd/jaeger/internal/integration/e2e/integration.go b/cmd/jaeger/internal/integration/e2e/integration.go deleted file mode 100644 index 8bf2ddf8d26..00000000000 --- a/cmd/jaeger/internal/integration/e2e/integration.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2024 The Jaeger Authors. -// SPDX-License-Identifier: Apache-2.0 - -package e2e - -import ( - "os" - "strings" - "testing" - - "github.com/open-telemetry/opentelemetry-collector-contrib/testbed/testbed" - "github.com/stretchr/testify/require" - - "github.com/jaegertracing/jaeger/cmd/jaeger/internal" - "github.com/jaegertracing/jaeger/plugin/storage/integration" -) - -// E2EStorageIntegration holds components for e2e mode of Jaeger-v2 -// storage integration test. The intended usage is as follows: -// - A specific storage implementation declares its own test functions -// (e.g. starts remote-storage). -// - In those functions, instantiates with e2eInitialize() -// and clean up with e2eCleanUp(). -// - Then calls RunTestSpanstore. -type E2EStorageIntegration struct { - integration.StorageIntegration - ConfigFile string - - runner testbed.OtelcolRunner - configCleanup func() -} - -// e2eInitialize starts the Jaeger-v2 collector with the provided config file, -// it also initialize the SpanWriter and SpanReader below. -func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { - factories, err := internal.Components() - require.NoError(t, err) - - config, err := os.ReadFile(s.ConfigFile) - require.NoError(t, err) - config = []byte(strings.Replace(string(config), "./cmd/jaeger/", "../../../", 1)) - - s.runner = testbed.NewInProcessCollector(factories) - s.configCleanup, err = s.runner.PrepareConfig(string(config)) - require.NoError(t, err) - require.NoError(t, s.runner.Start(testbed.StartParams{})) - - s.SpanWriter, err = createSpanWriter() - require.NoError(t, err) - s.SpanReader, err = createSpanReader() - require.NoError(t, err) -} - -func (s *E2EStorageIntegration) e2eCleanUp(t *testing.T) { - s.configCleanup() - _, err := s.runner.Stop() - require.NoError(t, err) -} diff --git a/cmd/jaeger/internal/integration/fixtures/generated_pict_pairs_spans.txt b/cmd/jaeger/internal/integration/fixtures/generated_pict_pairs_spans.txt deleted file mode 100644 index 138f2974806..00000000000 --- a/cmd/jaeger/internal/integration/fixtures/generated_pict_pairs_spans.txt +++ /dev/null @@ -1,307 +0,0 @@ -Parent Tracestate Kind Attributes Events Links Status -Root Empty Producer FaaSPubSub Empty Empty Ok -Child One Internal Internal Empty Empty Ok -Child Four Producer Empty Empty Empty Ok -Child One Unspecified gRPCServer Eight Empty Unset -Root Four Server HTTPServer One Empty Unset -Child Four Unspecified Internal Two Empty Ok -Root Four Producer Empty Empty Empty Unset -Child Four Server FaaSHTTP Empty Empty Unset -Child Empty Server FaaSHTTP Two Empty Error -Child Four Unspecified MessagingConsumer Two Empty Ok -Child One Producer MessagingProducer One Empty Unset -Child One Internal Internal Eight Empty Error -Child One Server FaaSTimer Eight Empty Ok -Root Empty Server FaaSTimer Two Empty Ok -Child Four Unspecified FaaSDatasource One Empty Ok -Child One Unspecified HTTPServer One Empty Ok -Root One Server gRPCServer One Empty Ok -Child Empty Unspecified DatabaseNoSQL One Empty Ok -Child One Client DatabaseNoSQL Empty Empty Error -Child Four Client HTTPClient Eight Empty Ok -Root Four Producer Empty One Empty Error -Root One Server FaaSTimer Empty Empty Ok -Child Four Consumer FaaSDatasource Eight Empty Unset -Child One Server FaaSOther Two Empty Ok -Child Four Server FaaSHTTP Two Empty Error -Child Four Unspecified gRPCClient Two Empty Ok -Root Four Producer MessagingProducer Two Empty Ok -Root One Server MaxCount Eight Empty Ok -Child Four Server MaxCount Two Empty Unset -Child One Client gRPCClient Empty Empty Error -Root Empty Producer FaaSPubSub One Empty Unset -Child Four Server FaaSTimer Two Empty Ok -Child One Producer MessagingProducer Two Empty Unset -Child Four Unspecified Empty One Empty Ok -Root One Server FaaSHTTP Eight Empty Error -Child Four Client DatabaseNoSQL Two Empty Ok -Child Empty Unspecified FaaSPubSub Eight Empty Ok -Child Empty Server gRPCServer Eight Empty Error -Child Four Client DatabaseNoSQL Two Empty Error -Root One Producer MessagingProducer One Empty Unset -Root One Server FaaSTimer One Empty Ok -Child Four Internal Internal Eight Empty Error -Child One Client DatabaseNoSQL Two Empty Error -Child Four Unspecified gRPCClient One Empty Error -Root Four Server FaaSTimer Empty Empty Ok -Child Four Client HTTPClient Eight Empty Error -Child Empty Unspecified Empty Two Empty Ok -Child One Server HTTPServer Eight Empty Ok -Child Empty Unspecified gRPCClient Empty Empty Ok -Child One Unspecified Internal Two Empty Error -Child Four Unspecified MessagingConsumer Empty Empty Ok -Child Empty Unspecified FaaSDatasource Empty Empty Error -Root One Producer MessagingProducer One Empty Ok -Child Four Unspecified DatabaseNoSQL One Empty Error -Root One Server HTTPServer Two Empty Unset -Child One Producer Empty Eight Empty Unset -Child One Unspecified DatabaseNoSQL One Empty Unset -Child Four Client DatabaseNoSQL Eight Empty Unset -Child Empty Unspecified Internal One Empty Ok -Child One Unspecified MessagingProducer Eight Empty Unset -Child Four Server FaaSOther Empty Empty Ok -Child Empty Consumer MessagingConsumer Eight Empty Unset -Child One Server FaaSOther Empty Empty Ok -Child Empty Unspecified MaxCount Empty Empty Error -Child Four Producer FaaSPubSub Two Empty Unset -Child One Client Empty One Empty Ok -Child Empty Producer Empty One Empty Unset -Child Four Client DatabaseSQL Eight Empty Error -Child Four Server FaaSOther Eight Empty Unset -Root Empty Server FaaSHTTP Two Empty Ok -Child One Client DatabaseSQL Eight Empty Unset -Child One Unspecified FaaSHTTP Empty Empty Error -Child One Unspecified MessagingProducer Eight Empty Ok -Child Empty Consumer MessagingConsumer Two Empty Unset -Child Empty Unspecified DatabaseNoSQL Two Empty Error -Child Four Unspecified FaaSDatasource Empty Empty Error -Child One Unspecified MaxCount Empty Empty Ok -Child One Unspecified FaaSDatasource Eight Empty Unset -Child Four Consumer MessagingConsumer Empty Empty Error -Child Four Client Empty Eight Empty Ok -Child One Consumer MessagingConsumer One Empty Error -Child One Client gRPCClient Eight Empty Error -Child Four Unspecified FaaSTimer Two Empty Error -Child One Client gRPCClient Eight Empty Ok -Child Empty Server FaaSHTTP Empty Empty Error -Child Four Unspecified FaaSOther Empty Empty Unset -Child Empty Consumer MessagingConsumer One Empty Ok -Child Four Client DatabaseSQL Eight Empty Unset -Child Four Unspecified MessagingProducer Empty Empty Ok -Child One Client DatabaseSQL Empty Empty Ok -Root Four Server FaaSOther Empty Empty Ok -Child Four Unspecified gRPCClient One Empty Unset -Child Empty Unspecified HTTPServer Two Empty Ok -Root One Producer Empty Eight Empty Ok -Child One Producer FaaSPubSub One Empty Error -Root Four Server FaaSHTTP Two Empty Ok -Child One Unspecified DatabaseSQL Two Empty Ok -Root Empty Server FaaSTimer Empty Empty Error -Child One Server MaxCount One Empty Unset -Child One Producer Empty Two Empty Unset -Child Four Server HTTPServer Eight Empty Unset -Root Empty Server HTTPServer One Empty Ok -Child Four Server FaaSOther One Empty Unset -Child One Producer Empty One Empty Unset -Child One Producer MessagingProducer One Empty Error -Child One Unspecified gRPCClient Two Empty Ok -Root Empty Server HTTPServer Eight Empty Error -Child Empty Unspecified FaaSHTTP One Empty Ok -Root One Server FaaSHTTP One Empty Unset -Child Four Unspecified MessagingConsumer Eight Empty Error -Child Empty Unspecified FaaSPubSub Two Empty Unset -Child Empty Client DatabaseSQL Two Empty Ok -Child Four Unspecified Empty Empty Empty Error -Child Empty Producer FaaSPubSub Eight Empty Ok -Child Four Producer Empty Two Empty Unset -Child Four Unspecified MessagingProducer Eight Empty Ok -Child Empty Unspecified MessagingProducer One Empty Error -Child Empty Server MaxCount One Empty Unset -Root Four Server FaaSHTTP One Empty Ok -Child Empty Server HTTPServer Two Empty Unset -Child One Unspecified Empty One Empty Ok -Child Empty Server gRPCServer Empty Empty Error -Child Four Unspecified gRPCServer One Empty Ok -Child Four Consumer MessagingConsumer Eight Empty Ok -Child Empty Unspecified DatabaseSQL Empty Empty Unset -Child Four Producer MessagingProducer Empty Empty Unset -Child One Client Empty Two Empty Error -Child Four Unspecified gRPCServer Empty Empty Unset -Child Empty Producer FaaSPubSub Two Empty Error -Child One Unspecified FaaSTimer Two Empty Unset -Child Empty Internal Internal One Empty Ok -Child One Client DatabaseSQL Empty Empty Error -Child Four Producer Empty Empty Empty Error -Child Four Unspecified FaaSTimer Empty Empty Unset -Child Four Client gRPCClient Two Empty Unset -Child Empty Unspecified Internal One Empty Error -Root Empty Server FaaSHTTP Empty Empty Error -Child Empty Server FaaSTimer Two Empty Unset -Child Four Consumer MessagingConsumer Empty Empty Unset -Child Empty Unspecified Internal Empty Empty Ok -Child Empty Producer MessagingProducer Two Empty Error -Child Four Client Empty Eight Empty Error -Child One Client DatabaseNoSQL One Empty Unset -Child Empty Client Empty Eight Empty Ok -Child One Server FaaSTimer One Empty Error -Child One Producer FaaSPubSub Eight Empty Ok -Child One Unspecified FaaSPubSub One Empty Ok -Child Empty Producer Empty Eight Empty Ok -Child Four Unspecified FaaSHTTP Empty Empty Unset -Child One Unspecified FaaSHTTP Empty Empty Ok -Child Empty Producer MessagingProducer One Empty Error -Child One Consumer FaaSDatasource One Empty Ok -Child One Client HTTPClient Eight Empty Error -Child Four Unspecified MaxCount Two Empty Ok -Root Empty Server HTTPServer Empty Empty Ok -Child Four Server HTTPServer Empty Empty Error -Child Four Internal Internal One Empty Error -Child Empty Server FaaSTimer Two Empty Error -Root Four Producer FaaSPubSub Two Empty Ok -Child Four Unspecified FaaSDatasource Eight Empty Error -Child One Unspecified DatabaseNoSQL Two Empty Error -Root One Server FaaSHTTP One Empty Error -Child Empty Server gRPCServer Two Empty Unset -Root Empty Producer Empty Empty Empty Error -Child Four Unspecified FaaSPubSub One Empty Error -Child One Unspecified FaaSHTTP Eight Empty Error -Child One Server MaxCount Two Empty Unset -Root Four Server FaaSOther Eight Empty Error -Child Empty Client DatabaseNoSQL Empty Empty Error -Child One Client Empty Empty Empty Error -Root Empty Server FaaSTimer One Empty Ok -Child One Consumer FaaSDatasource Empty Empty Ok -Child Empty Unspecified MessagingProducer Empty Empty Error -Child One Unspecified HTTPServer Eight Empty Error -Child Empty Server gRPCServer One Empty Ok -Child One Server MaxCount Empty Empty Error -Child Four Server FaaSOther Two Empty Error -Child Four Server HTTPServer One Empty Unset -Root One Server FaaSOther One Empty Error -Child Empty Unspecified FaaSOther One Empty Ok -Root Empty Server MaxCount Empty Empty Unset -Child Four Unspecified DatabaseSQL One Empty Unset -Child Four Unspecified Internal Two Empty Error -Child Four Unspecified DatabaseNoSQL Two Empty Unset -Child One Producer FaaSPubSub Eight Empty Error -Child Four Internal Internal Empty Empty Unset -Child Four Consumer FaaSDatasource Empty Empty Unset -Child One Client DatabaseNoSQL Two Empty Ok -Child Empty Producer FaaSPubSub One Empty Unset -Root One Server FaaSHTTP Two Empty Ok -Root Four Server gRPCServer Two Empty Ok -Child Four Unspecified DatabaseNoSQL Two Empty Error -Child Four Unspecified FaaSTimer Two Empty Unset -Child One Unspecified FaaSHTTP Eight Empty Ok -Child Empty Client DatabaseNoSQL Eight Empty Ok -Child Empty Client DatabaseSQL One Empty Error -Child Four Unspecified FaaSTimer One Empty Ok -Child Four Producer FaaSPubSub One Empty Error -Child One Server FaaSTimer Eight Empty Unset -Child Four Unspecified MessagingConsumer One Empty Unset -Root Four Server MaxCount Empty Empty Ok -Root Empty Server MaxCount Eight Empty Error -Child One Consumer FaaSDatasource Eight Empty Ok -Child Four Unspecified FaaSHTTP One Empty Error -Child Empty Server MaxCount One Empty Error -Child One Unspecified Internal One Empty Error -Child One Unspecified DatabaseSQL Eight Empty Unset -Root One Server FaaSTimer One Empty Error -Root Empty Server HTTPServer Eight Empty Unset -Child Four Server MaxCount One Empty Unset -Child One Unspecified FaaSTimer Eight Empty Error -Root Four Server FaaSTimer One Empty Unset -Root One Producer MessagingProducer Two Empty Error -Child Empty Unspecified gRPCServer Two Empty Ok -Child Four Server FaaSHTTP Two Empty Ok -Child Four Unspecified FaaSPubSub One Empty Unset -Child One Unspecified HTTPServer One Empty Unset -Child Empty Unspecified FaaSTimer One Empty Ok -Child One Unspecified gRPCServer Empty Empty Unset -Child Four Unspecified FaaSDatasource Two Empty Error -Child One Unspecified FaaSPubSub Empty Empty Unset -Root Four Server MaxCount Two Empty Error -Root Empty Producer MessagingProducer Empty Empty Ok -Child Four Unspecified FaaSOther Two Empty Unset -Child Four Server HTTPServer One Empty Error -Root Four Server HTTPServer Empty Empty Ok -Root One Server gRPCServer Two Empty Error -Root Four Server HTTPServer One Empty Error -Child Empty Client Empty Two Empty Error -Child Empty Unspecified MaxCount Empty Empty Unset -Child Empty Unspecified HTTPClient Eight Empty Error -Child Empty Producer Empty Eight Empty Unset -Child One Consumer FaaSDatasource One Empty Unset -Child Four Producer FaaSPubSub Empty Empty Error -Child One Consumer MessagingConsumer Two Empty Ok -Child Four Unspecified gRPCServer Two Empty Ok -Child One Unspecified FaaSHTTP Eight Empty Unset -Child Empty Client DatabaseNoSQL Empty Empty Ok -Child One Server FaaSHTTP One Empty Error -Child Four Unspecified MaxCount Eight Empty Ok -Child Four Unspecified MaxCount Empty Empty Ok -Child Empty Consumer FaaSDatasource Two Empty Error -Root One Server MaxCount Empty Empty Ok -Child Four Consumer FaaSDatasource One Empty Unset -Child Four Unspecified FaaSDatasource Empty Empty Ok -Child Four Unspecified gRPCClient Eight Empty Unset -Child One Unspecified DatabaseSQL One Empty Unset -Child One Internal Internal Two Empty Ok -Child Empty Client DatabaseNoSQL Two Empty Unset -Child Empty Unspecified gRPCServer Eight Empty Ok -Root One Server gRPCServer Empty Empty Ok -Child One Unspecified Empty Two Empty Error -Root Four Server MaxCount Two Empty Unset -Child Empty Unspecified HTTPClient Eight Empty Ok -Child One Server FaaSHTTP Empty Empty Ok -Child Four Unspecified HTTPServer One Empty Error -Child Empty Server FaaSOther One Empty Error -Child One Unspecified HTTPServer Empty Empty Unset -Child Four Unspecified HTTPClient Two Empty Unset -Child Empty Unspecified gRPCServer One Empty Unset -Child Empty Unspecified DatabaseNoSQL Eight Empty Unset -Child Four Unspecified HTTPServer Eight Empty Error -Root Empty Producer Empty Two Empty Ok -Root One Server gRPCServer Empty Empty Unset -Root One Producer FaaSPubSub Empty Empty Ok -Child Four Producer FaaSPubSub Empty Empty Ok -Child Four Unspecified Empty Eight Empty Ok -Child Empty Unspecified FaaSTimer One Empty Error -Child Four Unspecified FaaSTimer Two Empty Ok -Root One Server HTTPServer Eight Empty Error -Root Empty Server FaaSTimer One Empty Unset -Root Empty Server MaxCount Two Empty Unset -Child One Unspecified Empty Eight Empty Unset -Child One Unspecified HTTPServer Two Empty Ok -Child Empty Producer FaaSPubSub Empty Empty Ok -Root Four Server FaaSHTTP Eight Empty Error -Child Empty Unspecified Internal Eight Empty Ok -Child Four Unspecified HTTPServer Empty Empty Ok -Child One Client HTTPClient Two Empty Unset -Child One Unspecified FaaSTimer One Empty Unset -Child Empty Client Empty One Empty Unset -Child Four Unspecified MessagingConsumer Empty Empty Error -Child Empty Unspecified gRPCClient Eight Empty Error -Root Four Server FaaSOther Two Empty Error -Child Four Unspecified FaaSHTTP Eight Empty Ok -Child One Client Empty Eight Empty Ok -Child Empty Unspecified MaxCount Eight Empty Ok -Child Empty Unspecified FaaSOther One Empty Unset -Root Four Server FaaSTimer Eight Empty Unset -Child One Client DatabaseSQL One Empty Ok -Child Four Unspecified DatabaseNoSQL Empty Empty Unset -Child Empty Internal Internal Empty Empty Unset -Root Empty Server gRPCServer One Empty Ok -Child Four Server HTTPServer Two Empty Unset -Child One Unspecified MaxCount One Empty Unset -Child Four Client DatabaseSQL One Empty Error -Child Four Unspecified FaaSTimer One Empty Unset -Child One Unspecified gRPCClient One Empty Ok -Child Four Unspecified FaaSPubSub Eight Empty Ok -Root One Server FaaSOther Eight Empty Error -Child Empty Unspecified DatabaseSQL One Empty Error -Child Four Unspecified HTTPServer Eight Empty Unset -Root Four Server FaaSTimer Two Empty Error -Child One Unspecified MessagingConsumer Eight Empty Ok -Child Empty Unspecified HTTPServer Eight Empty Error -Root One Server gRPCServer Eight Empty Unset diff --git a/cmd/jaeger/internal/integration/fixtures/generated_pict_pairs_traces.txt b/cmd/jaeger/internal/integration/fixtures/generated_pict_pairs_traces.txt deleted file mode 100644 index ccbc54ba6c1..00000000000 --- a/cmd/jaeger/internal/integration/fixtures/generated_pict_pairs_traces.txt +++ /dev/null @@ -1,33 +0,0 @@ -Resource InstrumentationLibrary Spans -Empty None Several -K8sCloud None One -VMCloud None One -VMOnPrem None All -VMCloud None Several -Exec None One -VMCloud Two One -VMOnPrem One Several -K8sCloud One All -VMCloud Two None -Faas None Several -K8sCloud Two All -Empty Two None -Empty One None -VMCloud One All -VMOnPrem None One -K8sOnPrem Two Several -Empty Two Several -Exec Two Several -K8sOnPrem None One -K8sCloud One None -Empty None All -Empty None One -VMCloud None None -K8sCloud Two One -Exec None None -K8sOnPrem One Several -VMOnPrem Two All -VMOnPrem Two Several -K8sOnPrem One One -Faas One Several -Faas None One diff --git a/cmd/jaeger/internal/integration/fixtures/grpc_config.yaml b/cmd/jaeger/internal/integration/fixtures/grpc_config.yaml deleted file mode 100644 index 19cfedb693b..00000000000 --- a/cmd/jaeger/internal/integration/fixtures/grpc_config.yaml +++ /dev/null @@ -1,23 +0,0 @@ -service: - extensions: [jaeger_storage] - pipelines: - traces: - receivers: [otlp] - exporters: [jaeger_storage_exporter] - -extensions: - jaeger_storage: - grpc: - some-external-storage: - server: localhost:17271 - connection-timeout: 5s - -receivers: - otlp: - protocols: - grpc: - http: - -exporters: - jaeger_storage_exporter: - trace_storage: some-external-storage diff --git a/cmd/jaeger/internal/integration/fixtures/pict_input_spans.txt b/cmd/jaeger/internal/integration/fixtures/pict_input_spans.txt deleted file mode 100644 index 10cea9916ff..00000000000 --- a/cmd/jaeger/internal/integration/fixtures/pict_input_spans.txt +++ /dev/null @@ -1,14 +0,0 @@ -Parent: Root, Child -Tracestate: Empty, One, Four -Kind: Unspecified, Internal, Server, Client, Producer, Consumer -Attributes: Empty, DatabaseSQL, DatabaseNoSQL, FaaSDatasource, FaaSHTTP, FaaSPubSub, FaaSTimer, FaaSOther, HTTPClient, HTTPServer, MessagingProducer, MessagingConsumer, gRPCClient, gRPCServer, Internal, MaxCount -Events: Empty, One, Two, Eight -Links: Empty -Status: Unset, Ok, Error - -IF [Parent] = "Root" THEN [Kind] in {"Server", "Producer"}; -IF [Kind] = "Internal" THEN [Attributes] in {"Nil", "Internal"}; -IF [Kind] = "Server" THEN [Attributes] in {"Nil", "FaaSHTTP", "FaaSTimer", "FaaSOther", "HTTPServer", "gRPCServer", "MaxCount"}; -IF [Kind] = "Client" THEN [Attributes] in {"Empty", "DatabaseSQL", "DatabaseNoSQL", "HTTPClient", "gRPCClient"}; -IF [Kind] = "Producer" THEN [Attributes] in {"Empty", "MessagingProducer", "FaaSPubSub"}; -IF [Kind] = "Consumer" THEN [Attributes] in {"Nil", "MessagingConsumer", "FaaSDatasource"}; diff --git a/cmd/jaeger/internal/integration/fixtures/pict_input_traces.txt b/cmd/jaeger/internal/integration/fixtures/pict_input_traces.txt deleted file mode 100644 index 9eb0b9a29c1..00000000000 --- a/cmd/jaeger/internal/integration/fixtures/pict_input_traces.txt +++ /dev/null @@ -1,3 +0,0 @@ -Resource: Empty, VMOnPrem, VMCloud, K8sOnPrem, K8sCloud, Faas, Exec -InstrumentationLibrary: None, One, Two -Spans: None, One, Several, All diff --git a/cmd/jaeger/internal/integration/grpc_test.go b/cmd/jaeger/internal/integration/grpc_test.go index b5e5c4cca73..cf6029fd7a6 100644 --- a/cmd/jaeger/internal/integration/grpc_test.go +++ b/cmd/jaeger/internal/integration/grpc_test.go @@ -5,12 +5,46 @@ package integration import ( "testing" + + "github.com/stretchr/testify/require" + + "github.com/jaegertracing/jaeger/plugin/storage/integration" ) +type GRPCStorageIntegration struct { + E2EStorageIntegration + + server *integration.GRPCServer +} + +func (s *GRPCStorageIntegration) initialize(t *testing.T) { + var err error + s.server, err = integration.NewGRPCServer() + require.NoError(t, err) + require.NoError(t, s.server.Start()) + + s.Refresh = func(_ *testing.T) {} + s.CleanUp = s.cleanUp +} + +func (s *GRPCStorageIntegration) cleanUp(t *testing.T) { + require.NoError(t, s.server.Close()) + s.initialize(t) +} + func TestGRPCStorage(t *testing.T) { - s := &StorageIntegration{ - Name: "grpc", - ConfigFile: "fixtures/grpc_config.yaml", + integration.SkipUnlessEnv(t, "grpc") + + server, err := integration.NewGRPCServer() + require.NoError(t, err) + s := &GRPCStorageIntegration{ + server: server, } - s.Test(t) + s.ConfigFile = "../../grpc_config.yaml" + s.initialize(t) + s.e2eInitialize(t) + t.Cleanup(func() { + s.e2eCleanUp(t) + }) + s.RunSpanStoreTests(t) } diff --git a/cmd/jaeger/internal/integration/integration-diagram.png b/cmd/jaeger/internal/integration/integration-diagram.png deleted file mode 100644 index e4cd105bf22c7f9f14933e8511044f970383e404..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 307183 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

{SWPa`cb<~K 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 @@nRREz|dC6S=+mvnL*?BPt#Xlnm9y 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 diff --git a/cmd/jaeger/internal/integration/integration.go b/cmd/jaeger/internal/integration/integration.go index 22a5d1d3e09..5cd575b370b 100644 --- a/cmd/jaeger/internal/integration/integration.go +++ b/cmd/jaeger/internal/integration/integration.go @@ -4,129 +4,55 @@ package integration import ( - "context" "os" + "strings" "testing" - "time" "github.com/open-telemetry/opentelemetry-collector-contrib/testbed/testbed" "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/confmap" - "go.opentelemetry.io/collector/confmap/provider/fileprovider" - "go.opentelemetry.io/collector/otelcol" - "go.opentelemetry.io/collector/service/telemetry" "github.com/jaegertracing/jaeger/cmd/jaeger/internal" - "github.com/jaegertracing/jaeger/cmd/jaeger/internal/exporters/storageexporter" - "github.com/jaegertracing/jaeger/cmd/jaeger/internal/extension/jaegerstorage" - "github.com/jaegertracing/jaeger/cmd/jaeger/internal/integration/datareceivers" + "github.com/jaegertracing/jaeger/plugin/storage/integration" ) -type StorageIntegration struct { - Name string +// E2EStorageIntegration holds components for e2e mode of Jaeger-v2 +// storage integration test. The intended usage is as follows: +// - A specific storage implementation declares its own test functions +// (e.g. starts remote-storage). +// - In those functions, instantiates with e2eInitialize() +// and clean up with e2eCleanUp(). +// - Then calls RunTestSpanstore. +type E2EStorageIntegration struct { + integration.StorageIntegration ConfigFile string -} - -// The data receiver will utilize the artificial jaeger receiver to pull -// the traces from the database which is through jaeger storage extension. -// Because of that, we need to host another jaeger storage extension -// that is a duplication from the collector's extension. And get -// the exporter TraceStorage name to set it to receiver TraceStorage. -func (s *StorageIntegration) newDataReceiver(t *testing.T, factories otelcol.Factories) testbed.DataReceiver { - fmp := fileprovider.NewWithSettings(confmap.ProviderSettings{}) - configProvider, err := otelcol.NewConfigProvider( - otelcol.ConfigProviderSettings{ - ResolverSettings: confmap.ResolverSettings{ - URIs: []string{s.ConfigFile}, - Providers: map[string]confmap.Provider{fmp.Scheme(): fmp}, - }, - }, - ) - require.NoError(t, err) - cfg, err := configProvider.Get(context.Background(), factories) - require.NoError(t, err) - - tel, err := telemetry.New(context.Background(), telemetry.Settings{}, cfg.Service.Telemetry) - require.NoError(t, err) - - storageCfg, ok := cfg.Extensions[jaegerstorage.ID].(*jaegerstorage.Config) - require.True(t, ok, "no jaeger storage extension found in the config") - - exporterCfg, ok := cfg.Exporters[storageexporter.ID].(*storageexporter.Config) - require.True(t, ok, "no jaeger storage exporter found in the config") - - telemetrySettings := componenttest.NewNopTelemetrySettings() - telemetrySettings.Logger = tel.Logger() - - receiver := datareceivers.NewJaegerStorageDataReceiver( - telemetrySettings, - exporterCfg.TraceStorage, - storageCfg, - ) - return receiver + runner testbed.OtelcolRunner + configCleanup func() } -func (s *StorageIntegration) Test(t *testing.T) { - if os.Getenv("STORAGE") != s.Name { - t.Skipf("Integration test against Jaeger-V2 %[1]s skipped; set STORAGE env var to %[1]s to run this", s.Name) - } - +// e2eInitialize starts the Jaeger-v2 collector with the provided config file, +// it also initialize the SpanWriter and SpanReader below. +func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { factories, err := internal.Components() require.NoError(t, err) - dataProvider := testbed.NewGoldenDataProvider( - "fixtures/generated_pict_pairs_traces.txt", - "fixtures/generated_pict_pairs_spans.txt", - "", - ) - sender := testbed.NewOTLPTraceDataSender(testbed.DefaultHost, 4317) - receiver := s.newDataReceiver(t, factories) - - runner := testbed.NewInProcessCollector(factories) - validator := testbed.NewCorrectTestValidator( - sender.ProtocolName(), - receiver.ProtocolName(), - dataProvider, - ) - correctnessResults := &testbed.CorrectnessResults{} - config, err := os.ReadFile(s.ConfigFile) require.NoError(t, err) - t.Log(string(config)) + config = []byte(strings.Replace(string(config), "./cmd/jaeger/", "../../", 1)) - configCleanup, err := runner.PrepareConfig(string(config)) + s.runner = testbed.NewInProcessCollector(factories) + s.configCleanup, err = s.runner.PrepareConfig(string(config)) require.NoError(t, err) - defer configCleanup() - - tc := testbed.NewTestCase( - t, - dataProvider, - sender, - receiver, - runner, - validator, - correctnessResults, - ) - defer tc.Stop() - - tc.EnableRecording() - tc.StartBackend() - tc.StartAgent() - - tc.StartLoad(testbed.LoadOptions{ - DataItemsPerSecond: 1024, - ItemsPerBatch: 1, - }) - tc.Sleep(5 * time.Second) - tc.StopLoad() + require.NoError(t, s.runner.Start(testbed.StartParams{})) - tc.WaitForN(func() bool { - return tc.LoadGenerator.DataItemsSent() == tc.MockBackend.DataItemsReceived() - }, 10*time.Second, "all data items received") - - tc.StopBackend() + s.SpanWriter, err = createSpanWriter() + require.NoError(t, err) + s.SpanReader, err = createSpanReader() + require.NoError(t, err) +} - tc.ValidateData() +func (s *E2EStorageIntegration) e2eCleanUp(t *testing.T) { + s.configCleanup() + _, err := s.runner.Stop() + require.NoError(t, err) } diff --git a/cmd/jaeger/internal/integration/e2e/span_reader.go b/cmd/jaeger/internal/integration/span_reader.go similarity index 99% rename from cmd/jaeger/internal/integration/e2e/span_reader.go rename to cmd/jaeger/internal/integration/span_reader.go index 308c06388d7..bc33916a718 100644 --- a/cmd/jaeger/internal/integration/e2e/span_reader.go +++ b/cmd/jaeger/internal/integration/span_reader.go @@ -1,7 +1,7 @@ // Copyright (c) 2024 The Jaeger Authors. // SPDX-License-Identifier: Apache-2.0 -package e2e +package integration import ( "context" diff --git a/cmd/jaeger/internal/integration/e2e/span_writer.go b/cmd/jaeger/internal/integration/span_writer.go similarity index 98% rename from cmd/jaeger/internal/integration/e2e/span_writer.go rename to cmd/jaeger/internal/integration/span_writer.go index 53b25097713..edb10561acf 100644 --- a/cmd/jaeger/internal/integration/e2e/span_writer.go +++ b/cmd/jaeger/internal/integration/span_writer.go @@ -1,7 +1,7 @@ // Copyright (c) 2024 The Jaeger Authors. // SPDX-License-Identifier: Apache-2.0 -package e2e +package integration import ( "context" From 6a8955a8a667dc4fd8415127aa42e2492fe80d99 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 01:10:21 +0700 Subject: [PATCH 12/29] Refactor by changing in_process_collector to built binary Signed-off-by: James Ryans --- Makefile | 1 + cmd/jaeger/internal/integration/grpc_test.go | 2 +- .../internal/integration/integration.go | 39 +++++++++---------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 89e31d22269..7cf87a2770f 100644 --- a/Makefile +++ b/Makefile @@ -120,6 +120,7 @@ all-in-one-integration-test: # - grpc .PHONY: jaeger-v2-storage-integration-test jaeger-v2-storage-integration-test: + (cd cmd/jaeger/ && go build .) # Expire tests results for jaeger storage integration tests since the environment might change # even though the code remains the same. go clean -testcache diff --git a/cmd/jaeger/internal/integration/grpc_test.go b/cmd/jaeger/internal/integration/grpc_test.go index cf6029fd7a6..7ece27b4d32 100644 --- a/cmd/jaeger/internal/integration/grpc_test.go +++ b/cmd/jaeger/internal/integration/grpc_test.go @@ -40,7 +40,7 @@ func TestGRPCStorage(t *testing.T) { s := &GRPCStorageIntegration{ server: server, } - s.ConfigFile = "../../grpc_config.yaml" + s.ConfigFile = "cmd/jaeger/grpc_config.yaml" s.initialize(t) s.e2eInitialize(t) t.Cleanup(func() { diff --git a/cmd/jaeger/internal/integration/integration.go b/cmd/jaeger/internal/integration/integration.go index 5cd575b370b..2d26e1f268f 100644 --- a/cmd/jaeger/internal/integration/integration.go +++ b/cmd/jaeger/internal/integration/integration.go @@ -5,13 +5,11 @@ package integration import ( "os" - "strings" + "os/exec" "testing" - "github.com/open-telemetry/opentelemetry-collector-contrib/testbed/testbed" "github.com/stretchr/testify/require" - "github.com/jaegertracing/jaeger/cmd/jaeger/internal" "github.com/jaegertracing/jaeger/plugin/storage/integration" ) @@ -26,33 +24,32 @@ type E2EStorageIntegration struct { integration.StorageIntegration ConfigFile string - runner testbed.OtelcolRunner - configCleanup func() + binaryProcess *os.Process } // e2eInitialize starts the Jaeger-v2 collector with the provided config file, // it also initialize the SpanWriter and SpanReader below. func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { - factories, err := internal.Components() - require.NoError(t, err) - - config, err := os.ReadFile(s.ConfigFile) - require.NoError(t, err) - config = []byte(strings.Replace(string(config), "./cmd/jaeger/", "../../", 1)) - - s.runner = testbed.NewInProcessCollector(factories) - s.configCleanup, err = s.runner.PrepareConfig(string(config)) - require.NoError(t, err) - require.NoError(t, s.runner.Start(testbed.StartParams{})) - + cmd := exec.Cmd{ + Path: "./cmd/jaeger/jaeger", + Args: []string{"jaeger", "--config", s.ConfigFile}, + // Change the working directory to the root of this project + // since the binary config file jaeger_query's ui_config points to + // "./cmd/jaeger/config-ui.json" + Dir: "../../../..", + Stdout: os.Stderr, + Stderr: os.Stderr, + } + require.NoError(t, cmd.Start()) + s.binaryProcess = cmd.Process + + var err error s.SpanWriter, err = createSpanWriter() require.NoError(t, err) s.SpanReader, err = createSpanReader() require.NoError(t, err) } -func (s *E2EStorageIntegration) e2eCleanUp(t *testing.T) { - s.configCleanup() - _, err := s.runner.Stop() - require.NoError(t, err) +func (s *E2EStorageIntegration) e2eCleanUp(_ *testing.T) { + s.binaryProcess.Kill() } From dbb410791fcc26969d9a75c3644c69e566474463 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 16:15:11 +0700 Subject: [PATCH 13/29] Refactor spanWriter and spanReader to implement io.Closer Signed-off-by: James Ryans --- .../internal/integration/integration.go | 21 +++++--- .../internal/integration/span_reader.go | 30 ++++++++--- .../internal/integration/span_writer.go | 54 +++++++++++++++---- 3 files changed, 81 insertions(+), 24 deletions(-) diff --git a/cmd/jaeger/internal/integration/integration.go b/cmd/jaeger/internal/integration/integration.go index 2d26e1f268f..3a2938fadc7 100644 --- a/cmd/jaeger/internal/integration/integration.go +++ b/cmd/jaeger/internal/integration/integration.go @@ -4,6 +4,7 @@ package integration import ( + "io" "os" "os/exec" "testing" @@ -11,8 +12,11 @@ import ( "github.com/stretchr/testify/require" "github.com/jaegertracing/jaeger/plugin/storage/integration" + "github.com/jaegertracing/jaeger/ports" ) +const otlpPort = 4317 + // E2EStorageIntegration holds components for e2e mode of Jaeger-v2 // storage integration test. The intended usage is as follows: // - A specific storage implementation declares its own test functions @@ -43,13 +47,18 @@ func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { require.NoError(t, cmd.Start()) s.binaryProcess = cmd.Process - var err error - s.SpanWriter, err = createSpanWriter() - require.NoError(t, err) - s.SpanReader, err = createSpanReader() - require.NoError(t, err) + spanWriter := createSpanWriter(otlpPort) + require.NoError(t, spanWriter.Start()) + s.SpanWriter = spanWriter + + spanReader := createSpanReader(ports.QueryGRPC) + require.NoError(t, spanReader.Start()) + s.SpanReader = spanReader } -func (s *E2EStorageIntegration) e2eCleanUp(_ *testing.T) { +func (s *E2EStorageIntegration) e2eCleanUp(t *testing.T) { + require.NoError(t, s.SpanReader.(io.Closer).Close()) + require.NoError(t, s.SpanWriter.(io.Closer).Close()) + s.binaryProcess.Kill() } diff --git a/cmd/jaeger/internal/integration/span_reader.go b/cmd/jaeger/internal/integration/span_reader.go index bc33916a718..65623e4fa42 100644 --- a/cmd/jaeger/internal/integration/span_reader.go +++ b/cmd/jaeger/internal/integration/span_reader.go @@ -21,14 +21,26 @@ import ( "google.golang.org/grpc/status" ) -var _ spanstore.Reader = (*spanReader)(nil) +var ( + _ spanstore.Reader = (*spanReader)(nil) + _ io.Closer = (*spanReader)(nil) +) // SpanReader retrieve span data from Jaeger-v2 query with api_v2.QueryServiceClient. type spanReader struct { - client api_v2.QueryServiceClient + Port int + + clientConn *grpc.ClientConn + client api_v2.QueryServiceClient +} + +func createSpanReader(port int) *spanReader { + return &spanReader{ + Port: port, + } } -func createSpanReader() (*spanReader, error) { +func (r *spanReader) Start() error { opts := []grpc.DialOption{ grpc.WithBlock(), grpc.WithTransportCredentials(insecure.NewCredentials()), @@ -39,12 +51,16 @@ func createSpanReader() (*spanReader, error) { cc, err := grpc.DialContext(ctx, ports.PortToHostPort(ports.QueryGRPC), opts...) if err != nil { - return nil, err + return err } - return &spanReader{ - client: api_v2.NewQueryServiceClient(cc), - }, nil + r.clientConn = cc + r.client = api_v2.NewQueryServiceClient(cc) + return nil +} + +func (r *spanReader) Close() error { + return r.clientConn.Close() } func unwrapNotFoundErr(err error) error { diff --git a/cmd/jaeger/internal/integration/span_writer.go b/cmd/jaeger/internal/integration/span_writer.go index edb10561acf..a2c4f945c57 100644 --- a/cmd/jaeger/internal/integration/span_writer.go +++ b/cmd/jaeger/internal/integration/span_writer.go @@ -5,30 +5,62 @@ package integration import ( "context" + "fmt" + "io" "github.com/jaegertracing/jaeger/model" "github.com/jaegertracing/jaeger/storage/spanstore" jaeger2otlp "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" - "github.com/open-telemetry/opentelemetry-collector-contrib/testbed/testbed" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/config/configtls" + "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/exporter/exportertest" + "go.opentelemetry.io/collector/exporter/otlpexporter" + "go.uber.org/zap" ) -var _ spanstore.Writer = (*spanWriter)(nil) +var ( + _ spanstore.Writer = (*spanWriter)(nil) + _ io.Closer = (*spanWriter)(nil) +) // SpanWriter utilizes the OTLP exporter to send span data to the Jaeger-v2 receiver type spanWriter struct { - testbed.TraceDataSender + Port int + + exporter exporter.Traces } -func createSpanWriter() (*spanWriter, error) { - sender := testbed.NewOTLPTraceDataSender(testbed.DefaultHost, testbed.DefaultOTLPPort) - err := sender.Start() +func createSpanWriter(port int) *spanWriter { + return &spanWriter{ + Port: port, + } +} + +func (w *spanWriter) Start() error { + factory := otlpexporter.NewFactory() + cfg := factory.CreateDefaultConfig().(*otlpexporter.Config) + cfg.Endpoint = fmt.Sprintf("localhost:%d", w.Port) + cfg.RetryConfig.Enabled = false + cfg.QueueConfig.Enabled = false + cfg.TLSSetting = configtls.ClientConfig{ + Insecure: true, + } + + set := exportertest.NewNopCreateSettings() + set.Logger = zap.L() + + var err error + w.exporter, err = factory.CreateTracesExporter(context.Background(), set, cfg) if err != nil { - return nil, err + return err } - return &spanWriter{ - TraceDataSender: sender, - }, nil + return w.exporter.Start(context.Background(), componenttest.NewNopHost()) +} + +func (w *spanWriter) Close() error { + return w.exporter.Shutdown(context.Background()) } func (w *spanWriter) WriteSpan(ctx context.Context, span *model.Span) error { @@ -42,5 +74,5 @@ func (w *spanWriter) WriteSpan(ctx context.Context, span *model.Span) error { return err } - return w.ConsumeTraces(ctx, td) + return w.exporter.ConsumeTraces(ctx, td) } From 5e1e09342a57ac9001ea527b3442924c113842cc Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 16:17:34 +0700 Subject: [PATCH 14/29] Close grpc server at the end of the test Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/grpc_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/jaeger/internal/integration/grpc_test.go b/cmd/jaeger/internal/integration/grpc_test.go index 7ece27b4d32..9c36c6e764d 100644 --- a/cmd/jaeger/internal/integration/grpc_test.go +++ b/cmd/jaeger/internal/integration/grpc_test.go @@ -45,6 +45,7 @@ func TestGRPCStorage(t *testing.T) { s.e2eInitialize(t) t.Cleanup(func() { s.e2eCleanUp(t) + s.server.Close() }) s.RunSpanStoreTests(t) } From d6c1fbb096e1fc2c2c4042ebfa0ec16ade2e1586 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 16:18:03 +0700 Subject: [PATCH 15/29] Add goleak check Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/package_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 cmd/jaeger/internal/integration/package_test.go diff --git a/cmd/jaeger/internal/integration/package_test.go b/cmd/jaeger/internal/integration/package_test.go new file mode 100644 index 00000000000..f4fb9b5f972 --- /dev/null +++ b/cmd/jaeger/internal/integration/package_test.go @@ -0,0 +1,14 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package integration + +import ( + "testing" + + "github.com/jaegertracing/jaeger/pkg/testutils" +) + +func TestMain(m *testing.M) { + testutils.VerifyGoLeaks(m) +} From 0beaacac8cd02205039e59d7bccd75867a6a437f Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 21:00:33 +0700 Subject: [PATCH 16/29] Add error checking when shutting down grpc server Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/grpc_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/jaeger/internal/integration/grpc_test.go b/cmd/jaeger/internal/integration/grpc_test.go index 9c36c6e764d..d7c1e536fff 100644 --- a/cmd/jaeger/internal/integration/grpc_test.go +++ b/cmd/jaeger/internal/integration/grpc_test.go @@ -45,7 +45,7 @@ func TestGRPCStorage(t *testing.T) { s.e2eInitialize(t) t.Cleanup(func() { s.e2eCleanUp(t) - s.server.Close() + require.NoError(t, s.server.Close()) }) s.RunSpanStoreTests(t) } From e1aba5abd86c5c55b28df53005af043f5905cde7 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 21:47:43 +0700 Subject: [PATCH 17/29] Make GRPCServer.Start idempotent by closing it if already running Signed-off-by: James Ryans --- plugin/storage/integration/grpc_server.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugin/storage/integration/grpc_server.go b/plugin/storage/integration/grpc_server.go index 192b97f8355..83ef46a1e74 100644 --- a/plugin/storage/integration/grpc_server.go +++ b/plugin/storage/integration/grpc_server.go @@ -26,6 +26,12 @@ func NewGRPCServer() (*GRPCServer, error) { } func (s *GRPCServer) Start() error { + if s.server != nil { + if err := s.Close(); err != nil { + return err + } + } + memStorePlugin := grpcMemory.NewStoragePlugin(memory.NewStore(), memory.NewStore()) s.server = googleGRPC.NewServer() @@ -63,5 +69,6 @@ func (s *GRPCServer) Close() error { return err default: } + s.server = nil return nil } From 2dddea96047b2e6f618da6930b6e29bacf5ad2b4 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 21:51:26 +0700 Subject: [PATCH 18/29] Make GRPCServer.Close idempotent by checking if already closed Signed-off-by: James Ryans --- plugin/storage/integration/grpc_server.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugin/storage/integration/grpc_server.go b/plugin/storage/integration/grpc_server.go index 83ef46a1e74..7787f74d04b 100644 --- a/plugin/storage/integration/grpc_server.go +++ b/plugin/storage/integration/grpc_server.go @@ -62,13 +62,17 @@ func (s *GRPCServer) Start() error { } func (s *GRPCServer) Close() error { + if s.server == nil { + return nil + } + s.server.GracefulStop() + s.server = nil s.wg.Wait() select { case err := <-s.errChan: return err default: } - s.server = nil return nil } From 1ae55b2856ee93e0690928f84c99ae21bc086d0a Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 22:11:12 +0700 Subject: [PATCH 19/29] Chore fix span_writer and span_reader import format Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/span_reader.go | 7 ++++--- cmd/jaeger/internal/integration/span_writer.go | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd/jaeger/internal/integration/span_reader.go b/cmd/jaeger/internal/integration/span_reader.go index 65623e4fa42..8f3903f5537 100644 --- a/cmd/jaeger/internal/integration/span_reader.go +++ b/cmd/jaeger/internal/integration/span_reader.go @@ -12,13 +12,14 @@ import ( "strings" "time" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + "github.com/jaegertracing/jaeger/model" "github.com/jaegertracing/jaeger/ports" "github.com/jaegertracing/jaeger/proto-gen/api_v2" "github.com/jaegertracing/jaeger/storage/spanstore" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" ) var ( diff --git a/cmd/jaeger/internal/integration/span_writer.go b/cmd/jaeger/internal/integration/span_writer.go index a2c4f945c57..056c6cf6a3b 100644 --- a/cmd/jaeger/internal/integration/span_writer.go +++ b/cmd/jaeger/internal/integration/span_writer.go @@ -8,8 +8,6 @@ import ( "fmt" "io" - "github.com/jaegertracing/jaeger/model" - "github.com/jaegertracing/jaeger/storage/spanstore" jaeger2otlp "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config/configtls" @@ -17,6 +15,9 @@ import ( "go.opentelemetry.io/collector/exporter/exportertest" "go.opentelemetry.io/collector/exporter/otlpexporter" "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/model" + "github.com/jaegertracing/jaeger/storage/spanstore" ) var ( From 0f2ceb471d1568861c07a9c14ba6974782611acf Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 22:55:27 +0700 Subject: [PATCH 20/29] Fix the initialize() called twice Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/grpc_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmd/jaeger/internal/integration/grpc_test.go b/cmd/jaeger/internal/integration/grpc_test.go index d7c1e536fff..ec3f8f8d0c8 100644 --- a/cmd/jaeger/internal/integration/grpc_test.go +++ b/cmd/jaeger/internal/integration/grpc_test.go @@ -35,11 +35,7 @@ func (s *GRPCStorageIntegration) cleanUp(t *testing.T) { func TestGRPCStorage(t *testing.T) { integration.SkipUnlessEnv(t, "grpc") - server, err := integration.NewGRPCServer() - require.NoError(t, err) - s := &GRPCStorageIntegration{ - server: server, - } + s := &GRPCStorageIntegration{} s.ConfigFile = "cmd/jaeger/grpc_config.yaml" s.initialize(t) s.e2eInitialize(t) From 992542a083ec00bf8c8276c4407f229e4bbf1fa5 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Fri, 5 Apr 2024 22:57:01 +0700 Subject: [PATCH 21/29] Move cmd.Process.Kill() to inside t.CleanUp Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/integration.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd/jaeger/internal/integration/integration.go b/cmd/jaeger/internal/integration/integration.go index 3a2938fadc7..74a28e3bd23 100644 --- a/cmd/jaeger/internal/integration/integration.go +++ b/cmd/jaeger/internal/integration/integration.go @@ -27,8 +27,6 @@ const otlpPort = 4317 type E2EStorageIntegration struct { integration.StorageIntegration ConfigFile string - - binaryProcess *os.Process } // e2eInitialize starts the Jaeger-v2 collector with the provided config file, @@ -45,7 +43,9 @@ func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { Stderr: os.Stderr, } require.NoError(t, cmd.Start()) - s.binaryProcess = cmd.Process + t.Cleanup(func() { + require.NoError(t, cmd.Process.Kill()) + }) spanWriter := createSpanWriter(otlpPort) require.NoError(t, spanWriter.Start()) @@ -59,6 +59,4 @@ func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { func (s *E2EStorageIntegration) e2eCleanUp(t *testing.T) { require.NoError(t, s.SpanReader.(io.Closer).Close()) require.NoError(t, s.SpanWriter.(io.Closer).Close()) - - s.binaryProcess.Kill() } From 4fb4ffff399cd250beeeec5ad68333bc27c0845f Mon Sep 17 00:00:00 2001 From: James Ryans Date: Sat, 6 Apr 2024 16:49:42 +0700 Subject: [PATCH 22/29] Avoid using channel in GRPCServer Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/grpc_test.go | 4 +--- plugin/storage/integration/grpc_server.go | 25 +++++++------------- plugin/storage/integration/grpc_test.go | 3 +-- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/cmd/jaeger/internal/integration/grpc_test.go b/cmd/jaeger/internal/integration/grpc_test.go index ec3f8f8d0c8..52e446592ad 100644 --- a/cmd/jaeger/internal/integration/grpc_test.go +++ b/cmd/jaeger/internal/integration/grpc_test.go @@ -18,9 +18,7 @@ type GRPCStorageIntegration struct { } func (s *GRPCStorageIntegration) initialize(t *testing.T) { - var err error - s.server, err = integration.NewGRPCServer() - require.NoError(t, err) + s.server = integration.NewGRPCServer() require.NoError(t, s.server.Start()) s.Refresh = func(_ *testing.T) {} diff --git a/plugin/storage/integration/grpc_server.go b/plugin/storage/integration/grpc_server.go index 7787f74d04b..c974060702d 100644 --- a/plugin/storage/integration/grpc_server.go +++ b/plugin/storage/integration/grpc_server.go @@ -16,13 +16,13 @@ import ( ) type GRPCServer struct { - errChan chan error - server *googleGRPC.Server - wg sync.WaitGroup + server *googleGRPC.Server + serveErr error + wg sync.WaitGroup } -func NewGRPCServer() (*GRPCServer, error) { - return &GRPCServer{errChan: make(chan error, 1)}, nil +func NewGRPCServer() *GRPCServer { + return &GRPCServer{} } func (s *GRPCServer) Start() error { @@ -51,12 +51,7 @@ func (s *GRPCServer) Start() error { s.wg.Add(1) go func() { defer s.wg.Done() - if err = s.server.Serve(listener); err != nil { - select { - case s.errChan <- err: - default: - } - } + s.serveErr = s.server.Serve(listener) }() return nil } @@ -69,10 +64,6 @@ func (s *GRPCServer) Close() error { s.server.GracefulStop() s.server = nil s.wg.Wait() - select { - case err := <-s.errChan: - return err - default: - } - return nil + + return s.serveErr } diff --git a/plugin/storage/integration/grpc_test.go b/plugin/storage/integration/grpc_test.go index b0d9b33d1e5..60fddc06803 100644 --- a/plugin/storage/integration/grpc_test.go +++ b/plugin/storage/integration/grpc_test.go @@ -133,8 +133,7 @@ func TestGRPCRemoteStorage(t *testing.T) { "--grpc-storage.server=localhost:17271", "--grpc-storage.tls.enabled=false", } - server, err := NewGRPCServer() - require.NoError(t, err) + server := NewGRPCServer() s := &GRPCStorageIntegrationTestSuite{ flags: flags, From f2cea9f88c8272e849183c4742d1f41822f34ec4 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Sun, 7 Apr 2024 21:12:35 +0700 Subject: [PATCH 23/29] Refactor GRPCServer to reuse remote-storage server Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/grpc_test.go | 14 ++-- plugin/storage/integration/grpc_server.go | 69 ------------------- plugin/storage/integration/grpc_test.go | 23 ++++--- .../integration/remote_memory_storage.go | 63 +++++++++++++++++ 4 files changed, 84 insertions(+), 85 deletions(-) delete mode 100644 plugin/storage/integration/grpc_server.go create mode 100644 plugin/storage/integration/remote_memory_storage.go diff --git a/cmd/jaeger/internal/integration/grpc_test.go b/cmd/jaeger/internal/integration/grpc_test.go index 52e446592ad..ff58b95cb5a 100644 --- a/cmd/jaeger/internal/integration/grpc_test.go +++ b/cmd/jaeger/internal/integration/grpc_test.go @@ -8,25 +8,29 @@ import ( "github.com/stretchr/testify/require" + "github.com/jaegertracing/jaeger/pkg/testutils" "github.com/jaegertracing/jaeger/plugin/storage/integration" ) type GRPCStorageIntegration struct { E2EStorageIntegration - server *integration.GRPCServer + remoteStorage *integration.RemoteMemoryStorage } func (s *GRPCStorageIntegration) initialize(t *testing.T) { - s.server = integration.NewGRPCServer() - require.NoError(t, s.server.Start()) + logger, _ := testutils.NewLogger() + + var err error + s.remoteStorage, err = integration.StartNewRemoteMemoryStorage(logger) + require.NoError(t, err) s.Refresh = func(_ *testing.T) {} s.CleanUp = s.cleanUp } func (s *GRPCStorageIntegration) cleanUp(t *testing.T) { - require.NoError(t, s.server.Close()) + require.NoError(t, s.remoteStorage.Close()) s.initialize(t) } @@ -39,7 +43,7 @@ func TestGRPCStorage(t *testing.T) { s.e2eInitialize(t) t.Cleanup(func() { s.e2eCleanUp(t) - require.NoError(t, s.server.Close()) + require.NoError(t, s.remoteStorage.Close()) }) s.RunSpanStoreTests(t) } diff --git a/plugin/storage/integration/grpc_server.go b/plugin/storage/integration/grpc_server.go deleted file mode 100644 index c974060702d..00000000000 --- a/plugin/storage/integration/grpc_server.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2024 The Jaeger Authors. -// SPDX-License-Identifier: Apache-2.0 - -package integration - -import ( - "net" - "sync" - - googleGRPC "google.golang.org/grpc" - - grpcMemory "github.com/jaegertracing/jaeger/plugin/storage/grpc/memory" - "github.com/jaegertracing/jaeger/plugin/storage/grpc/shared" - "github.com/jaegertracing/jaeger/plugin/storage/memory" - "github.com/jaegertracing/jaeger/ports" -) - -type GRPCServer struct { - server *googleGRPC.Server - serveErr error - wg sync.WaitGroup -} - -func NewGRPCServer() *GRPCServer { - return &GRPCServer{} -} - -func (s *GRPCServer) Start() error { - if s.server != nil { - if err := s.Close(); err != nil { - return err - } - } - - memStorePlugin := grpcMemory.NewStoragePlugin(memory.NewStore(), memory.NewStore()) - - s.server = googleGRPC.NewServer() - queryPlugin := shared.StorageGRPCPlugin{ - Impl: memStorePlugin, - ArchiveImpl: memStorePlugin, - } - - if err := queryPlugin.RegisterHandlers(s.server); err != nil { - return err - } - - listener, err := net.Listen("tcp", ports.PortToHostPort(ports.RemoteStorageGRPC)) - if err != nil { - return err - } - s.wg.Add(1) - go func() { - defer s.wg.Done() - s.serveErr = s.server.Serve(listener) - }() - return nil -} - -func (s *GRPCServer) Close() error { - if s.server == nil { - return nil - } - - s.server.GracefulStop() - s.server = nil - s.wg.Wait() - - return s.serveErr -} diff --git a/plugin/storage/integration/grpc_test.go b/plugin/storage/integration/grpc_test.go index 60fddc06803..d8b83a0b9ee 100644 --- a/plugin/storage/integration/grpc_test.go +++ b/plugin/storage/integration/grpc_test.go @@ -36,17 +36,19 @@ const ( type GRPCStorageIntegrationTestSuite struct { StorageIntegration - logger *zap.Logger - flags []string - factory *grpc.Factory - server *GRPCServer + logger *zap.Logger + flags []string + factory *grpc.Factory + useRemoteStorage bool + remoteStorage *RemoteMemoryStorage } func (s *GRPCStorageIntegrationTestSuite) initialize(t *testing.T) { s.logger, _ = testutils.NewLogger() - if s.server != nil { - err := s.server.Start() + if s.useRemoteStorage { + var err error + s.remoteStorage, err = StartNewRemoteMemoryStorage(s.logger) require.NoError(t, err) } @@ -76,8 +78,8 @@ func (s *GRPCStorageIntegrationTestSuite) initialize(t *testing.T) { func (s *GRPCStorageIntegrationTestSuite) cleanUp(t *testing.T) { require.NoError(t, s.factory.Close()) - if s.server != nil { - require.NoError(t, s.server.Close()) + if s.useRemoteStorage { + require.NoError(t, s.remoteStorage.Close()) } s.initialize(t) } @@ -133,11 +135,10 @@ func TestGRPCRemoteStorage(t *testing.T) { "--grpc-storage.server=localhost:17271", "--grpc-storage.tls.enabled=false", } - server := NewGRPCServer() s := &GRPCStorageIntegrationTestSuite{ - flags: flags, - server: server, + flags: flags, + useRemoteStorage: true, } s.initialize(t) s.RunAll(t) diff --git a/plugin/storage/integration/remote_memory_storage.go b/plugin/storage/integration/remote_memory_storage.go new file mode 100644 index 00000000000..2a833793575 --- /dev/null +++ b/plugin/storage/integration/remote_memory_storage.go @@ -0,0 +1,63 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package integration + +import ( + "os" + + "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/cmd/remote-storage/app" + "github.com/jaegertracing/jaeger/pkg/config" + "github.com/jaegertracing/jaeger/pkg/healthcheck" + "github.com/jaegertracing/jaeger/pkg/metrics" + "github.com/jaegertracing/jaeger/pkg/tenancy" + "github.com/jaegertracing/jaeger/plugin/storage" + "github.com/jaegertracing/jaeger/ports" +) + +type RemoteMemoryStorage struct { + server *app.Server + storageFactory *storage.Factory +} + +func StartNewRemoteMemoryStorage(logger *zap.Logger) (*RemoteMemoryStorage, error) { + opts := &app.Options{ + GRPCHostPort: ports.PortToHostPort(ports.RemoteStorageGRPC), + Tenancy: tenancy.Options{ + Enabled: false, + }, + } + tm := tenancy.NewManager(&opts.Tenancy) + storageFactory, err := storage.NewFactory(storage.FactoryConfigFromEnvAndCLI(os.Args, os.Stderr)) + if err != nil { + return nil, err + } + v, _ := config.Viperize(storageFactory.AddFlags) + storageFactory.InitFromViper(v, logger) + err = storageFactory.Initialize(metrics.NullFactory, logger) + if err != nil { + return nil, err + } + + server, err := app.NewServer(opts, storageFactory, tm, logger, healthcheck.New()) + if err != nil { + return nil, err + } + if err := server.Start(); err != nil { + return nil, err + } + + return &RemoteMemoryStorage{ + server: server, + storageFactory: storageFactory, + }, nil +} + +func (s *RemoteMemoryStorage) Close() error { + if err := s.server.Close(); err != nil { + return err + } + return s.storageFactory.Close() +} From 9b5b1477f9e2c4d04990854bb541627c7f00cee7 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Sun, 7 Apr 2024 21:34:51 +0700 Subject: [PATCH 24/29] Add comment to integration.go to clarify the usage Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/integration.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cmd/jaeger/internal/integration/integration.go b/cmd/jaeger/internal/integration/integration.go index 74a28e3bd23..067b4fff14c 100644 --- a/cmd/jaeger/internal/integration/integration.go +++ b/cmd/jaeger/internal/integration/integration.go @@ -19,11 +19,14 @@ const otlpPort = 4317 // E2EStorageIntegration holds components for e2e mode of Jaeger-v2 // storage integration test. The intended usage is as follows: -// - A specific storage implementation declares its own test functions +// - Initialize a specific storage implementation declares its own test functions // (e.g. starts remote-storage). -// - In those functions, instantiates with e2eInitialize() -// and clean up with e2eCleanUp(). -// - Then calls RunTestSpanstore. +// - Then, instantiates with e2eInitialize() to run the Jaeger-v2 collector +// and also the SpanWriter and SpanReader. +// - After that, calls RunSpanStoreTests(). +// - Clean up with e2eCleanup() to close the SpanReader and SpanWriter connections. +// - At last, clean up anything declared in its own test functions. +// (e.g. close remote-storage) type E2EStorageIntegration struct { integration.StorageIntegration ConfigFile string @@ -31,6 +34,7 @@ type E2EStorageIntegration struct { // e2eInitialize starts the Jaeger-v2 collector with the provided config file, // it also initialize the SpanWriter and SpanReader below. +// This function should be called before any of the tests start. func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { cmd := exec.Cmd{ Path: "./cmd/jaeger/jaeger", @@ -56,6 +60,8 @@ func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { s.SpanReader = spanReader } +// e2eCleanUp closes the SpanReader and SpanWriter gRPC connection. +// This function should be called after all the tests are finished. func (s *E2EStorageIntegration) e2eCleanUp(t *testing.T) { require.NoError(t, s.SpanReader.(io.Closer).Close()) require.NoError(t, s.SpanWriter.(io.Closer).Close()) From 18876bd3073193c6ee45bff5725b81419b9e836e Mon Sep 17 00:00:00 2001 From: James Ryans Date: Sun, 7 Apr 2024 21:50:07 +0700 Subject: [PATCH 25/29] Refactor spanWriter and spanReader to start on creation Signed-off-by: James Ryans --- .../internal/integration/integration.go | 15 ++++++----- .../internal/integration/span_reader.go | 22 ++++++++++++---- .../internal/integration/span_writer.go | 26 ++++++++----------- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/cmd/jaeger/internal/integration/integration.go b/cmd/jaeger/internal/integration/integration.go index 067b4fff14c..67ecd609508 100644 --- a/cmd/jaeger/internal/integration/integration.go +++ b/cmd/jaeger/internal/integration/integration.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/jaegertracing/jaeger/pkg/testutils" "github.com/jaegertracing/jaeger/plugin/storage/integration" "github.com/jaegertracing/jaeger/ports" ) @@ -36,6 +37,8 @@ type E2EStorageIntegration struct { // it also initialize the SpanWriter and SpanReader below. // This function should be called before any of the tests start. func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { + logger, _ := testutils.NewLogger() + cmd := exec.Cmd{ Path: "./cmd/jaeger/jaeger", Args: []string{"jaeger", "--config", s.ConfigFile}, @@ -51,13 +54,11 @@ func (s *E2EStorageIntegration) e2eInitialize(t *testing.T) { require.NoError(t, cmd.Process.Kill()) }) - spanWriter := createSpanWriter(otlpPort) - require.NoError(t, spanWriter.Start()) - s.SpanWriter = spanWriter - - spanReader := createSpanReader(ports.QueryGRPC) - require.NoError(t, spanReader.Start()) - s.SpanReader = spanReader + var err error + s.SpanWriter, err = createSpanWriter(logger, otlpPort) + require.NoError(t, err) + s.SpanReader, err = createSpanReader(ports.QueryGRPC) + require.NoError(t, err) } // e2eCleanUp closes the SpanReader and SpanWriter gRPC connection. diff --git a/cmd/jaeger/internal/integration/span_reader.go b/cmd/jaeger/internal/integration/span_reader.go index 8f3903f5537..90d9a9fb15f 100644 --- a/cmd/jaeger/internal/integration/span_reader.go +++ b/cmd/jaeger/internal/integration/span_reader.go @@ -29,16 +29,28 @@ var ( // SpanReader retrieve span data from Jaeger-v2 query with api_v2.QueryServiceClient. type spanReader struct { - Port int - clientConn *grpc.ClientConn client api_v2.QueryServiceClient } -func createSpanReader(port int) *spanReader { - return &spanReader{ - Port: port, +func createSpanReader(port int) (*spanReader, error) { + opts := []grpc.DialOption{ + grpc.WithBlock(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + cc, err := grpc.DialContext(ctx, ports.PortToHostPort(port), opts...) + if err != nil { + return nil, err } + + return &spanReader{ + clientConn: cc, + client: api_v2.NewQueryServiceClient(cc), + }, nil } func (r *spanReader) Start() error { diff --git a/cmd/jaeger/internal/integration/span_writer.go b/cmd/jaeger/internal/integration/span_writer.go index 056c6cf6a3b..a6fff35e402 100644 --- a/cmd/jaeger/internal/integration/span_writer.go +++ b/cmd/jaeger/internal/integration/span_writer.go @@ -27,21 +27,13 @@ var ( // SpanWriter utilizes the OTLP exporter to send span data to the Jaeger-v2 receiver type spanWriter struct { - Port int - exporter exporter.Traces } -func createSpanWriter(port int) *spanWriter { - return &spanWriter{ - Port: port, - } -} - -func (w *spanWriter) Start() error { +func createSpanWriter(logger *zap.Logger, port int) (*spanWriter, error) { factory := otlpexporter.NewFactory() cfg := factory.CreateDefaultConfig().(*otlpexporter.Config) - cfg.Endpoint = fmt.Sprintf("localhost:%d", w.Port) + cfg.Endpoint = fmt.Sprintf("localhost:%d", port) cfg.RetryConfig.Enabled = false cfg.QueueConfig.Enabled = false cfg.TLSSetting = configtls.ClientConfig{ @@ -49,15 +41,19 @@ func (w *spanWriter) Start() error { } set := exportertest.NewNopCreateSettings() - set.Logger = zap.L() + set.Logger = logger - var err error - w.exporter, err = factory.CreateTracesExporter(context.Background(), set, cfg) + exporter, err := factory.CreateTracesExporter(context.Background(), set, cfg) if err != nil { - return err + return nil, err + } + if err := exporter.Start(context.Background(), componenttest.NewNopHost()); err != nil { + return nil, err } - return w.exporter.Start(context.Background(), componenttest.NewNopHost()) + return &spanWriter{ + exporter: exporter, + }, nil } func (w *spanWriter) Close() error { From ca75924011227da330b14aedc30c2ba78774b04a Mon Sep 17 00:00:00 2001 From: James Ryans Date: Sun, 7 Apr 2024 21:58:21 +0700 Subject: [PATCH 26/29] Fix ci-grpc v1 should define env SPAN_STORAGE_TYPE=memory Signed-off-by: James Ryans --- .github/workflows/ci-grpc.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-grpc.yml b/.github/workflows/ci-grpc.yml index e523f88f618..482b2bfbe1f 100644 --- a/.github/workflows/ci-grpc.yml +++ b/.github/workflows/ci-grpc.yml @@ -37,7 +37,8 @@ jobs: run: | case ${{ matrix.version }} in v1) - make grpc-storage-integration-test + SPAN_STORAGE_TYPE=memory \ + make grpc-storage-integration-test ;; v2) STORAGE=grpc \ From 29558651ef927d51b844593e99688c31dc78221c Mon Sep 17 00:00:00 2001 From: James Ryans Date: Tue, 9 Apr 2024 00:24:56 +0700 Subject: [PATCH 27/29] Temporarily skip testing trace binary type tags Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/grpc_test.go | 2 ++ plugin/storage/integration/integration.go | 38 +++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/cmd/jaeger/internal/integration/grpc_test.go b/cmd/jaeger/internal/integration/grpc_test.go index ff58b95cb5a..d70a61ba226 100644 --- a/cmd/jaeger/internal/integration/grpc_test.go +++ b/cmd/jaeger/internal/integration/grpc_test.go @@ -39,6 +39,8 @@ func TestGRPCStorage(t *testing.T) { s := &GRPCStorageIntegration{} s.ConfigFile = "cmd/jaeger/grpc_config.yaml" + s.SkipBinaryAttrs = true + s.initialize(t) s.e2eInitialize(t) t.Cleanup(func() { diff --git a/plugin/storage/integration/integration.go b/plugin/storage/integration/integration.go index b1483e49633..912be145dcb 100644 --- a/plugin/storage/integration/integration.go +++ b/plugin/storage/integration/integration.go @@ -75,6 +75,10 @@ type StorageIntegration struct { // Skip Archive Test if not supported by the storage backend SkipArchiveTest bool + // TODO: remove this after upstream issue in OTEL jaeger translator is fixed + // Skip testing trace binary tags, logs, and process + SkipBinaryAttrs bool + // List of tests which has to be skipped, it can be regex too. SkipList []string @@ -369,7 +373,39 @@ func (s *StorageIntegration) loadParseAndWriteLargeTrace(t *testing.T) *model.Tr func (s *StorageIntegration) getTraceFixture(t *testing.T, fixture string) *model.Trace { fileName := fmt.Sprintf("fixtures/traces/%s.json", fixture) - return getTraceFixtureExact(t, fileName) + trace := getTraceFixtureExact(t, fileName) + + if s.SkipBinaryAttrs { + t.Logf("Dropped binary type attributes from trace ID: %s", trace.Spans[0].TraceID.String()) + trace = s.dropBinaryAttrs(t, trace) + } + + return trace +} + +func (s *StorageIntegration) dropBinaryAttrs(t *testing.T, trace *model.Trace) *model.Trace { + for _, span := range trace.Spans { + span.Tags = s.dropBinaryTags(t, span.Tags) + span.Process.Tags = s.dropBinaryTags(t, span.Process.Tags) + + for i := range span.Logs { + span.Logs[i].Fields = s.dropBinaryTags(t, span.Logs[i].Fields) + } + } + + return trace +} + +func (s *StorageIntegration) dropBinaryTags(_ *testing.T, tags []model.KeyValue) []model.KeyValue { + newTags := make([]model.KeyValue, 0) + for _, tag := range tags { + if tag.VType == model.ValueType_BINARY { + continue + } + newTags = append(newTags, tag) + } + + return newTags } func getTraceFixtureExact(t *testing.T, fileName string) *model.Trace { From 779dff877ba7a7317bdec679d4ccbd6fbbbcf593 Mon Sep 17 00:00:00 2001 From: James Ryans Date: Tue, 9 Apr 2024 00:38:59 +0700 Subject: [PATCH 28/29] Delete unused spanReader Start func Signed-off-by: James Ryans --- .../internal/integration/span_reader.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/cmd/jaeger/internal/integration/span_reader.go b/cmd/jaeger/internal/integration/span_reader.go index 90d9a9fb15f..9d2d61b9658 100644 --- a/cmd/jaeger/internal/integration/span_reader.go +++ b/cmd/jaeger/internal/integration/span_reader.go @@ -53,25 +53,6 @@ func createSpanReader(port int) (*spanReader, error) { }, nil } -func (r *spanReader) Start() error { - opts := []grpc.DialOption{ - grpc.WithBlock(), - grpc.WithTransportCredentials(insecure.NewCredentials()), - } - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - cc, err := grpc.DialContext(ctx, ports.PortToHostPort(ports.QueryGRPC), opts...) - if err != nil { - return err - } - - r.clientConn = cc - r.client = api_v2.NewQueryServiceClient(cc) - return nil -} - func (r *spanReader) Close() error { return r.clientConn.Close() } From 5610e5c45eda9fda6c76883faabc9e9d2771c1df Mon Sep 17 00:00:00 2001 From: James Ryans Date: Tue, 9 Apr 2024 09:40:18 +0700 Subject: [PATCH 29/29] Refactor remote_memory_storage to use *testing.T Signed-off-by: James Ryans --- cmd/jaeger/internal/integration/grpc_test.go | 10 ++---- plugin/storage/integration/grpc_test.go | 6 ++-- .../integration/remote_memory_storage.go | 32 +++++++------------ 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/cmd/jaeger/internal/integration/grpc_test.go b/cmd/jaeger/internal/integration/grpc_test.go index d70a61ba226..0fd2c9743db 100644 --- a/cmd/jaeger/internal/integration/grpc_test.go +++ b/cmd/jaeger/internal/integration/grpc_test.go @@ -6,8 +6,6 @@ package integration import ( "testing" - "github.com/stretchr/testify/require" - "github.com/jaegertracing/jaeger/pkg/testutils" "github.com/jaegertracing/jaeger/plugin/storage/integration" ) @@ -21,16 +19,14 @@ type GRPCStorageIntegration struct { func (s *GRPCStorageIntegration) initialize(t *testing.T) { logger, _ := testutils.NewLogger() - var err error - s.remoteStorage, err = integration.StartNewRemoteMemoryStorage(logger) - require.NoError(t, err) + s.remoteStorage = integration.StartNewRemoteMemoryStorage(t, logger) s.Refresh = func(_ *testing.T) {} s.CleanUp = s.cleanUp } func (s *GRPCStorageIntegration) cleanUp(t *testing.T) { - require.NoError(t, s.remoteStorage.Close()) + s.remoteStorage.Close(t) s.initialize(t) } @@ -45,7 +41,7 @@ func TestGRPCStorage(t *testing.T) { s.e2eInitialize(t) t.Cleanup(func() { s.e2eCleanUp(t) - require.NoError(t, s.remoteStorage.Close()) + s.remoteStorage.Close(t) }) s.RunSpanStoreTests(t) } diff --git a/plugin/storage/integration/grpc_test.go b/plugin/storage/integration/grpc_test.go index d8b83a0b9ee..46358b62b02 100644 --- a/plugin/storage/integration/grpc_test.go +++ b/plugin/storage/integration/grpc_test.go @@ -47,9 +47,7 @@ func (s *GRPCStorageIntegrationTestSuite) initialize(t *testing.T) { s.logger, _ = testutils.NewLogger() if s.useRemoteStorage { - var err error - s.remoteStorage, err = StartNewRemoteMemoryStorage(s.logger) - require.NoError(t, err) + s.remoteStorage = StartNewRemoteMemoryStorage(t, s.logger) } f := grpc.NewFactory() @@ -79,7 +77,7 @@ func (s *GRPCStorageIntegrationTestSuite) initialize(t *testing.T) { func (s *GRPCStorageIntegrationTestSuite) cleanUp(t *testing.T) { require.NoError(t, s.factory.Close()) if s.useRemoteStorage { - require.NoError(t, s.remoteStorage.Close()) + s.remoteStorage.Close(t) } s.initialize(t) } diff --git a/plugin/storage/integration/remote_memory_storage.go b/plugin/storage/integration/remote_memory_storage.go index 2a833793575..a15020e4ae5 100644 --- a/plugin/storage/integration/remote_memory_storage.go +++ b/plugin/storage/integration/remote_memory_storage.go @@ -5,7 +5,9 @@ package integration import ( "os" + "testing" + "github.com/stretchr/testify/require" "go.uber.org/zap" "github.com/jaegertracing/jaeger/cmd/remote-storage/app" @@ -22,7 +24,7 @@ type RemoteMemoryStorage struct { storageFactory *storage.Factory } -func StartNewRemoteMemoryStorage(logger *zap.Logger) (*RemoteMemoryStorage, error) { +func StartNewRemoteMemoryStorage(t *testing.T, logger *zap.Logger) *RemoteMemoryStorage { opts := &app.Options{ GRPCHostPort: ports.PortToHostPort(ports.RemoteStorageGRPC), Tenancy: tenancy.Options{ @@ -31,33 +33,23 @@ func StartNewRemoteMemoryStorage(logger *zap.Logger) (*RemoteMemoryStorage, erro } tm := tenancy.NewManager(&opts.Tenancy) storageFactory, err := storage.NewFactory(storage.FactoryConfigFromEnvAndCLI(os.Args, os.Stderr)) - if err != nil { - return nil, err - } + require.NoError(t, err) + v, _ := config.Viperize(storageFactory.AddFlags) storageFactory.InitFromViper(v, logger) - err = storageFactory.Initialize(metrics.NullFactory, logger) - if err != nil { - return nil, err - } + require.NoError(t, storageFactory.Initialize(metrics.NullFactory, logger)) server, err := app.NewServer(opts, storageFactory, tm, logger, healthcheck.New()) - if err != nil { - return nil, err - } - if err := server.Start(); err != nil { - return nil, err - } + require.NoError(t, err) + require.NoError(t, server.Start()) return &RemoteMemoryStorage{ server: server, storageFactory: storageFactory, - }, nil + } } -func (s *RemoteMemoryStorage) Close() error { - if err := s.server.Close(); err != nil { - return err - } - return s.storageFactory.Close() +func (s *RemoteMemoryStorage) Close(t *testing.T) { + require.NoError(t, s.server.Close()) + require.NoError(t, s.storageFactory.Close()) }