From 520634370c9ea1bd6dd04b0fe23a681b912112f3 Mon Sep 17 00:00:00 2001 From: Joshua MacDonald Date: Tue, 16 Jul 2024 12:34:50 -0700 Subject: [PATCH] Remove the primary exporter/receiver components, update references and documentation --- CONTRIBUTING.md | 9 +- Makefile | 2 +- README.md | 4 +- collector/README.md | 22 +- collector/cmd/otelarrowcol/components.go | 23 +- collector/cmd/otelarrowcol/go.mod | 171 +-- collector/cmd/otelarrowcol/go.sum | 334 ++-- collector/exporter/otelarrowexporter/Makefile | 1 - .../exporter/otelarrowexporter/README.md | 250 --- .../exporter/otelarrowexporter/config.go | 126 -- .../exporter/otelarrowexporter/config_test.go | 155 -- collector/exporter/otelarrowexporter/doc.go | 6 - .../exporter/otelarrowexporter/factory.go | 146 -- .../otelarrowexporter/factory_test.go | 238 --- .../generated_component_test.go | 18 - .../generated_package_test.go | 12 - collector/exporter/otelarrowexporter/go.mod | 98 -- collector/exporter/otelarrowexporter/go.sum | 252 --- .../otelarrowexporter/internal/arrow/Makefile | 6 - .../internal/arrow/README.md | 127 -- .../internal/arrow/bestofn.go | 157 -- .../internal/arrow/common_test.go | 413 ----- .../internal/arrow/design.png | Bin 149806 -> 0 bytes .../internal/arrow/exporter.go | 370 ----- .../internal/arrow/exporter_test.go | 908 ----------- .../internal/arrow/grpcmock/credentials.go | 74 - .../internal/arrow/prioritizer.go | 108 -- .../internal/arrow/stream.go | 471 ------ .../internal/arrow/stream_test.go | 352 ----- .../internal/metadata/generated_status.go | 17 - .../exporter/otelarrowexporter/metadata.yaml | 15 - .../exporter/otelarrowexporter/otelarrow.go | 335 ---- .../otelarrowexporter/otelarrow_test.go | 1191 --------------- .../otelarrowexporter/testdata/config.yaml | 33 - .../otelarrowexporter/testdata/default.yaml | 1 - .../otelarrowexporter/testdata/test_cert.pem | 17 - .../otelarrowexporter/testdata/test_key.pem | 28 - collector/otelarrowcol-build.yaml | 20 +- .../receiver/otelarrowreceiver/README.md | 199 --- .../receiver/otelarrowreceiver/config.go | 55 - .../receiver/otelarrowreceiver/config_test.go | 135 -- collector/receiver/otelarrowreceiver/doc.go | 6 - .../receiver/otelarrowreceiver/factory.go | 121 -- .../otelarrowreceiver/factory_test.go | 216 --- .../generated_component_test.go | 83 - .../generated_package_test.go | 12 - collector/receiver/otelarrowreceiver/go.mod | 100 -- collector/receiver/otelarrowreceiver/go.sum | 257 ---- .../otelarrowreceiver/internal/arrow/Makefile | 7 - .../otelarrowreceiver/internal/arrow/arrow.go | 899 ----------- .../internal/arrow/arrow_test.go | 1361 ----------------- .../internal/arrow/mock/auth.go | 84 - .../internal/arrow/mock/consumer.go | 174 --- .../otelarrowreceiver/internal/logs/otlp.go | 48 - .../internal/logs/otlp_test.go | 95 -- .../internal/metadata/generated_status.go | 17 - .../internal/metadata/generated_telemetry.go | 17 - .../metadata/generated_telemetry_test.go | 63 - .../internal/metrics/otlp.go | 48 - .../internal/metrics/otlp_test.go | 96 -- .../otelarrowreceiver/internal/trace/otlp.go | 49 - .../internal/trace/otlp_test.go | 93 -- .../receiver/otelarrowreceiver/metadata.yaml | 7 - .../receiver/otelarrowreceiver/otelarrow.go | 212 --- .../otelarrowreceiver/otelarrow_test.go | 676 -------- .../testdata/bad_proto_config.yaml | 3 - .../otelarrowreceiver/testdata/config.yaml | 31 - .../otelarrowreceiver/testdata/default.yaml | 1 - .../otelarrowreceiver/testdata/only_grpc.yaml | 3 - .../testdata/typo_default_proto_config.yaml | 3 - .../otelarrowreceiver/testdata/uds.yaml | 5 - collector/test/e2e_test.go | 4 +- collector/test/go.mod | 52 +- collector/test/go.sum | 98 +- versions.yaml | 2 - 75 files changed, 380 insertions(+), 11462 deletions(-) delete mode 100644 collector/exporter/otelarrowexporter/Makefile delete mode 100644 collector/exporter/otelarrowexporter/README.md delete mode 100644 collector/exporter/otelarrowexporter/config.go delete mode 100644 collector/exporter/otelarrowexporter/config_test.go delete mode 100644 collector/exporter/otelarrowexporter/doc.go delete mode 100644 collector/exporter/otelarrowexporter/factory.go delete mode 100644 collector/exporter/otelarrowexporter/factory_test.go delete mode 100644 collector/exporter/otelarrowexporter/generated_component_test.go delete mode 100644 collector/exporter/otelarrowexporter/generated_package_test.go delete mode 100644 collector/exporter/otelarrowexporter/go.mod delete mode 100644 collector/exporter/otelarrowexporter/go.sum delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/Makefile delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/README.md delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/bestofn.go delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/common_test.go delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/design.png delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/exporter.go delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/exporter_test.go delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/grpcmock/credentials.go delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/prioritizer.go delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/stream.go delete mode 100644 collector/exporter/otelarrowexporter/internal/arrow/stream_test.go delete mode 100644 collector/exporter/otelarrowexporter/internal/metadata/generated_status.go delete mode 100644 collector/exporter/otelarrowexporter/metadata.yaml delete mode 100644 collector/exporter/otelarrowexporter/otelarrow.go delete mode 100644 collector/exporter/otelarrowexporter/otelarrow_test.go delete mode 100644 collector/exporter/otelarrowexporter/testdata/config.yaml delete mode 100644 collector/exporter/otelarrowexporter/testdata/default.yaml delete mode 100644 collector/exporter/otelarrowexporter/testdata/test_cert.pem delete mode 100644 collector/exporter/otelarrowexporter/testdata/test_key.pem delete mode 100644 collector/receiver/otelarrowreceiver/README.md delete mode 100644 collector/receiver/otelarrowreceiver/config.go delete mode 100644 collector/receiver/otelarrowreceiver/config_test.go delete mode 100644 collector/receiver/otelarrowreceiver/doc.go delete mode 100644 collector/receiver/otelarrowreceiver/factory.go delete mode 100644 collector/receiver/otelarrowreceiver/factory_test.go delete mode 100644 collector/receiver/otelarrowreceiver/generated_component_test.go delete mode 100644 collector/receiver/otelarrowreceiver/generated_package_test.go delete mode 100644 collector/receiver/otelarrowreceiver/go.mod delete mode 100644 collector/receiver/otelarrowreceiver/go.sum delete mode 100644 collector/receiver/otelarrowreceiver/internal/arrow/Makefile delete mode 100644 collector/receiver/otelarrowreceiver/internal/arrow/arrow.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/arrow/arrow_test.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/arrow/mock/auth.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/arrow/mock/consumer.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/logs/otlp.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/logs/otlp_test.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/metadata/generated_status.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/metadata/generated_telemetry.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/metadata/generated_telemetry_test.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/metrics/otlp.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/metrics/otlp_test.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/trace/otlp.go delete mode 100644 collector/receiver/otelarrowreceiver/internal/trace/otlp_test.go delete mode 100644 collector/receiver/otelarrowreceiver/metadata.yaml delete mode 100644 collector/receiver/otelarrowreceiver/otelarrow.go delete mode 100644 collector/receiver/otelarrowreceiver/otelarrow_test.go delete mode 100644 collector/receiver/otelarrowreceiver/testdata/bad_proto_config.yaml delete mode 100644 collector/receiver/otelarrowreceiver/testdata/config.yaml delete mode 100644 collector/receiver/otelarrowreceiver/testdata/default.yaml delete mode 100644 collector/receiver/otelarrowreceiver/testdata/only_grpc.yaml delete mode 100644 collector/receiver/otelarrowreceiver/testdata/typo_default_proto_config.yaml delete mode 100644 collector/receiver/otelarrowreceiver/testdata/uds.yaml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7229f430..7bae2114 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,8 +25,9 @@ definition and Golang libraries for producing and consuming streams of data in this format. Exporter and receiver components for the [OpenTelemetry -Collector][OTCDOCS] were developed in parallel and are currently -maintained in this repository. +Collector][OTCDOCS] were developed in parallel, maintained in this +repository through release v0.24.0, and now they are included in +release of the OpenTelemetry Collector-Contrib repository. - [Exporter][EXPORTER]: Send telemetry data using OpenTelemetry Protocol with Apache Arrow - [Receiver][RECEIVER]: Receive telemetry data using OpenTelemetry Protocol with Apache Arrow. @@ -79,8 +80,8 @@ the easiest way to upgrade this repository is: [OTCDOCS]: https://opentelemetry.io/docs/collector/ [OTCGH]: https://github.com/open-telemetry/opentelemetry-collector [OACGH]: https://github.com/open-telemetry/otel-arrow-collector -[EXPORTER]: ./collector/exporter/otelarrowexporter/README.md -[RECEIVER]: ./collector/receiver/otelarrowreceiver/README.md +[EXPORTER]: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/otelarrowexporter/README.md +[RECEIVER]: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/otelarrowreceiver/README.md [DONATION]: https://github.com/open-telemetry/community/issues/1332 [DEVPROCESS]: https://github.com/open-telemetry/otel-arrow-collector/issues/48 [OTLPRECEIVER]: https://github.com/open-telemetry/opentelemetry-collector/receiver/otlpreceiver diff --git a/Makefile b/Makefile index fd14fc2e..ac99f3e1 100644 --- a/Makefile +++ b/Makefile @@ -103,7 +103,7 @@ endif BUILDER = builder .PHONY: $(BUILDER) builder: - $(GOCMD) install go.opentelemetry.io/collector/cmd/builder@v0.102.1 + $(GOCMD) install go.opentelemetry.io/collector/cmd/builder@v0.105.0 .PHONY: genotelarrowcol genotelarrowcol: builder diff --git a/README.md b/README.md index 42ffa3df..ff5e19bd 100644 --- a/README.md +++ b/README.md @@ -107,8 +107,8 @@ workloads. We are pleased to release two new collector components, presently housed in this this repository. -- [OpenTelemetry Protocol with Apache Arrow Receiver](./collector/receiver/otelarrowreceiver/README.md) -- [OpenTelemetry Protocol with Apache Arrow Exporter](./collector/exporter/otelarrowexporter/README.md) +- [OpenTelemetry Protocol with Apache Arrow Receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/otelarrowreceiver/README.md) +- [OpenTelemetry Protocol with Apache Arrow Exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/otelarrowexporter/README.md) We are working with the maintainers of the [OpenTelemetry Collector-Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib) diff --git a/collector/README.md b/collector/README.md index a207b833..6a459f46 100644 --- a/collector/README.md +++ b/collector/README.md @@ -11,20 +11,8 @@ The primary components are: ## Building and distributing these components -We are aware that building and distributing OpenTelemetry collectors -is not a simple task and have prepared dedicated instructions for -building and testing the components in this repository. - -[Instructions for building an OpenTelemetry Collector with support for -OpenTelemetry Protocol with Apache Arrow.][BUILDING] - -After you have built a collector using one of the documented methods, -see the [examples][EXAMPLES]. - -[We would prefer to include these components in the OpenTelemetry -Contrib Collector, because it is an officially maintained artifact. -At this time, however, these components are new and the migration -process will take some time to complete.][CONTRIBUTION] +The exporter and receiver components are included in the official +OpenTelemetry Collector-Contrib release images since v0.105.0. ## Components included in this repository @@ -71,11 +59,6 @@ From the core collector repository: - [otelhttpexporter][UPSTREAMHTTPOTLP]: Useful for debugging, sends standard OTLP over HTTP - [debugexporter][UPSTREAMDEBUG]: Useful for debugging, prints OTLP data to the console -Also, the build includes a synthetic telemetry data generator: - -- [generator][GENERATOR]: Produces synthetic telemetry data. - - [BUILDING]: ./BUILDING.md [EXPORTER]: ./exporter/otelarrowexporter/README.md [RECEIVER]: ./receiver/otelarrowreceiver/README.md @@ -93,5 +76,4 @@ Also, the build includes a synthetic telemetry data generator: [PPROFEXT]: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/extension/pprofextension/README.md [UPSTREAMHTTPOTLP]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/otlphttpexporter/README.md [UPSTREAMDEBUG]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/debugexporter/README.md -[GENERATOR]: https://github.com/lightstep/telemetry-generator/blob/main/README.md [EXAMPLES]: ./examples/README.md diff --git a/collector/cmd/otelarrowcol/components.go b/collector/cmd/otelarrowcol/components.go index 4b3de7d3..1e3678d7 100644 --- a/collector/cmd/otelarrowcol/components.go +++ b/collector/cmd/otelarrowcol/components.go @@ -3,6 +3,7 @@ package main import ( + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/connector" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/extension" @@ -10,7 +11,7 @@ import ( "go.opentelemetry.io/collector/processor" "go.opentelemetry.io/collector/receiver" validationconnector "github.com/open-telemetry/otel-arrow/collector/connector/validationconnector" - otelarrowexporter "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter" + otelarrowexporter "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter" debugexporter "go.opentelemetry.io/collector/exporter/debugexporter" otlphttpexporter "go.opentelemetry.io/collector/exporter/otlphttpexporter" fileexporter "github.com/open-telemetry/otel-arrow/collector/exporter/fileexporter" @@ -19,7 +20,7 @@ import ( pprofextension "github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension" concurrentbatchprocessor "github.com/open-telemetry/otel-arrow/collector/processor/concurrentbatchprocessor" obfuscationprocessor "github.com/open-telemetry/otel-arrow/collector/processor/obfuscationprocessor" - otelarrowreceiver "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver" + otelarrowreceiver "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver" filereceiver "github.com/open-telemetry/otel-arrow/collector/receiver/filereceiver" otlpreceiver "go.opentelemetry.io/collector/receiver/otlpreceiver" ) @@ -36,6 +37,10 @@ func components() (otelcol.Factories, error) { if err != nil { return otelcol.Factories{}, err } + factories.ExtensionModules = make(map[component.Type]string, len(factories.Extensions)) + factories.ExtensionModules[headerssetterextension.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.104.0" + factories.ExtensionModules[basicauthextension.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.104.0" + factories.ExtensionModules[pprofextension.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.104.0" factories.Receivers, err = receiver.MakeFactoryMap( otelarrowreceiver.NewFactory(), @@ -45,6 +50,10 @@ func components() (otelcol.Factories, error) { if err != nil { return otelcol.Factories{}, err } + factories.ReceiverModules = make(map[component.Type]string, len(factories.Receivers)) + factories.ReceiverModules[otelarrowreceiver.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver v0.104.0" + factories.ReceiverModules[filereceiver.NewFactory().Type()] = "github.com/open-telemetry/otel-arrow/collector/receiver/filereceiver v0.24.0" + factories.ReceiverModules[otlpreceiver.NewFactory().Type()] = "go.opentelemetry.io/collector/receiver/otlpreceiver v0.105.0" factories.Exporters, err = exporter.MakeFactoryMap( otelarrowexporter.NewFactory(), @@ -55,6 +64,11 @@ func components() (otelcol.Factories, error) { if err != nil { return otelcol.Factories{}, err } + factories.ExporterModules = make(map[component.Type]string, len(factories.Exporters)) + factories.ExporterModules[otelarrowexporter.NewFactory().Type()] = "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter v0.104.0" + factories.ExporterModules[debugexporter.NewFactory().Type()] = "go.opentelemetry.io/collector/exporter/debugexporter v0.105.0" + factories.ExporterModules[otlphttpexporter.NewFactory().Type()] = "go.opentelemetry.io/collector/exporter/otlphttpexporter v0.105.0" + factories.ExporterModules[fileexporter.NewFactory().Type()] = "github.com/open-telemetry/otel-arrow/collector/exporter/fileexporter v0.24.0" factories.Processors, err = processor.MakeFactoryMap( concurrentbatchprocessor.NewFactory(), @@ -63,6 +77,9 @@ func components() (otelcol.Factories, error) { if err != nil { return otelcol.Factories{}, err } + factories.ProcessorModules = make(map[component.Type]string, len(factories.Processors)) + factories.ProcessorModules[concurrentbatchprocessor.NewFactory().Type()] = "github.com/open-telemetry/otel-arrow/collector/processor/concurrentbatchprocessor v0.24.0" + factories.ProcessorModules[obfuscationprocessor.NewFactory().Type()] = "github.com/open-telemetry/otel-arrow/collector/processor/obfuscationprocessor v0.24.0" factories.Connectors, err = connector.MakeFactoryMap( validationconnector.NewFactory(), @@ -70,6 +87,8 @@ func components() (otelcol.Factories, error) { if err != nil { return otelcol.Factories{}, err } + factories.ConnectorModules = make(map[component.Type]string, len(factories.Connectors)) + factories.ConnectorModules[validationconnector.NewFactory().Type()] = "github.com/open-telemetry/otel-arrow/collector/connector/validationconnector v0.24.0" return factories, nil } diff --git a/collector/cmd/otelarrowcol/go.mod b/collector/cmd/otelarrowcol/go.mod index 027a9bc9..6130eea1 100644 --- a/collector/cmd/otelarrowcol/go.mod +++ b/collector/cmd/otelarrowcol/go.mod @@ -4,37 +4,37 @@ module github.com/open-telemetry/otel-arrow/collector/cmd/otelarrowcol go 1.21.0 -toolchain go1.22.2 +toolchain go1.22.5 require ( - github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.102.0 - github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.102.0 - github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.102.0 + github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter v0.104.0 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.104.0 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.104.0 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.104.0 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver v0.104.0 github.com/open-telemetry/otel-arrow/collector/connector/validationconnector v0.24.0 github.com/open-telemetry/otel-arrow/collector/exporter/fileexporter v0.24.0 - github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter v0.24.0 github.com/open-telemetry/otel-arrow/collector/processor/concurrentbatchprocessor v0.24.0 github.com/open-telemetry/otel-arrow/collector/processor/obfuscationprocessor v0.24.0 github.com/open-telemetry/otel-arrow/collector/receiver/filereceiver v0.24.0 - github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver v0.24.0 - go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/confmap/converter/expandconverter v0.102.1 - go.opentelemetry.io/collector/confmap/provider/envprovider v0.102.1 - go.opentelemetry.io/collector/confmap/provider/fileprovider v0.102.1 - go.opentelemetry.io/collector/confmap/provider/httpprovider v0.102.1 - go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.102.1 - go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.102.1 - go.opentelemetry.io/collector/connector v0.102.2-0.20240612162315-964e3a95872e - go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e - go.opentelemetry.io/collector/exporter/debugexporter v0.102.1 - go.opentelemetry.io/collector/exporter/otlphttpexporter v0.102.1 - go.opentelemetry.io/collector/extension v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/otelcol v0.102.1 - go.opentelemetry.io/collector/processor v0.102.2-0.20240612162315-964e3a95872e - go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e - go.opentelemetry.io/collector/receiver/otlpreceiver v0.102.1 - golang.org/x/sys v0.21.0 + go.opentelemetry.io/collector/component v0.105.0 + go.opentelemetry.io/collector/confmap v0.105.0 + go.opentelemetry.io/collector/confmap/converter/expandconverter v0.105.0 + go.opentelemetry.io/collector/confmap/provider/envprovider v0.105.0 + go.opentelemetry.io/collector/confmap/provider/fileprovider v0.105.0 + go.opentelemetry.io/collector/confmap/provider/httpprovider v0.105.0 + go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.105.0 + go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.105.0 + go.opentelemetry.io/collector/connector v0.105.0 + go.opentelemetry.io/collector/exporter v0.105.0 + go.opentelemetry.io/collector/exporter/debugexporter v0.105.0 + go.opentelemetry.io/collector/exporter/otlphttpexporter v0.105.0 + go.opentelemetry.io/collector/extension v0.105.0 + go.opentelemetry.io/collector/otelcol v0.105.0 + go.opentelemetry.io/collector/processor v0.105.0 + go.opentelemetry.io/collector/receiver v0.105.0 + go.opentelemetry.io/collector/receiver/otlpreceiver v0.105.0 + golang.org/x/sys v0.22.0 ) require ( @@ -58,7 +58,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.1 // indirect @@ -75,7 +75,7 @@ require ( github.com/holiman/uint256 v1.2.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.8 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect github.com/knadh/koanf/providers/confmap v0.1.0 // indirect @@ -89,6 +89,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mostynb/go-grpc-compression v1.2.3 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/open-telemetry/otel-arrow v0.24.0 // indirect github.com/open-telemetry/otel-arrow/collector v0.24.0 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect @@ -96,12 +97,12 @@ require ( github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.54.0 // indirect - github.com/prometheus/procfs v0.15.0 // indirect - github.com/rs/cors v1.10.1 // indirect - github.com/shirou/gopsutil/v3 v3.24.4 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rs/cors v1.11.0 // indirect + github.com/shirou/gopsutil/v4 v4.24.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/testify v1.9.0 // indirect github.com/tg123/go-htpasswd v1.2.2 // indirect @@ -115,75 +116,61 @@ require ( github.com/zeebo/xxh3 v1.0.2 // indirect go.mongodb.org/mongo-driver v1.12.1 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configcompression v1.9.1-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/confighttp v0.102.1 // indirect - go.opentelemetry.io/collector/config/confignet v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configopaque v1.9.1-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configretry v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/internal v0.102.1 // indirect - go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/featuregate v1.9.0 // indirect - go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/semconv v0.102.1 // indirect - go.opentelemetry.io/collector/service v0.102.1 // indirect - go.opentelemetry.io/contrib/config v0.7.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.27.0 // indirect - go.opentelemetry.io/otel v1.27.0 // indirect - go.opentelemetry.io/otel/bridge/opencensus v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.49.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 // indirect - go.opentelemetry.io/otel/metric v1.27.0 // indirect - go.opentelemetry.io/otel/sdk v1.27.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect - go.opentelemetry.io/otel/trace v1.27.0 // indirect - go.opentelemetry.io/proto/otlp v1.2.0 // indirect + go.opentelemetry.io/collector v0.105.0 // indirect + go.opentelemetry.io/collector/config/configauth v0.105.0 // indirect + go.opentelemetry.io/collector/config/configcompression v1.12.0 // indirect + go.opentelemetry.io/collector/config/configgrpc v0.105.0 // indirect + go.opentelemetry.io/collector/config/confighttp v0.105.0 // indirect + go.opentelemetry.io/collector/config/confignet v0.105.0 // indirect + go.opentelemetry.io/collector/config/configopaque v1.12.0 // indirect + go.opentelemetry.io/collector/config/configretry v1.12.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect + go.opentelemetry.io/collector/config/configtls v1.12.0 // indirect + go.opentelemetry.io/collector/config/internal v0.105.0 // indirect + go.opentelemetry.io/collector/consumer v0.105.0 // indirect + go.opentelemetry.io/collector/extension/auth v0.105.0 // indirect + go.opentelemetry.io/collector/featuregate v1.12.0 // indirect + go.opentelemetry.io/collector/internal/globalgates v0.105.0 // indirect + go.opentelemetry.io/collector/pdata v1.12.0 // indirect + go.opentelemetry.io/collector/semconv v0.105.0 // indirect + go.opentelemetry.io/collector/service v0.105.0 // indirect + go.opentelemetry.io/contrib/config v0.8.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.28.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/bridge/opencensus v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.50.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 // indirect + go.opentelemetry.io/otel/log v0.4.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.4.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gonum.org/v1/gonum v0.15.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect - google.golang.org/grpc v1.64.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - -replace github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver => ../../receiver/otelarrowreceiver - -replace github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter => ../../exporter/otelarrowexporter - -replace github.com/open-telemetry/otel-arrow/collector/exporter/fileexporter => ../../exporter/fileexporter - -replace github.com/open-telemetry/otel-arrow/collector/connector/validationconnector => ../../connector/validationconnector - -replace github.com/open-telemetry/otel-arrow/collector => ../.. - -replace github.com/open-telemetry/otel-arrow/collector/receiver/filereceiver => ../../receiver/filereceiver - -replace github.com/open-telemetry/otel-arrow/collector/processor/obfuscationprocessor => ../../processor/obfuscationprocessor - -replace github.com/open-telemetry/otel-arrow/collector/processor/concurrentbatchprocessor => ../../processor/concurrentbatchprocessor - -replace github.com/open-telemetry/otel-arrow => ../../.. diff --git a/collector/cmd/otelarrowcol/go.sum b/collector/cmd/otelarrowcol/go.sum index e99b214f..fb7364ec 100644 --- a/collector/cmd/otelarrowcol/go.sum +++ b/collector/cmd/otelarrowcol/go.sum @@ -211,7 +211,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI= github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -337,8 +337,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= @@ -638,8 +638,8 @@ github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrD github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= @@ -753,6 +753,8 @@ github.com/mostynb/go-grpc-compression v1.2.3 h1:42/BKWMy0KEJGSdWvzqIyOZ95YcR9mL github.com/mostynb/go-grpc-compression v1.2.3/go.mod h1:AghIxF3P57umzqM9yz795+y1Vjs47Km/Y2FE6ouQ7Lg= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= @@ -791,14 +793,32 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.102.0 h1:eTRvylkBSoJchyrHgfsvq00hxkzKpntz1JL44MmR/a4= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.102.0/go.mod h1:Muh6W6zAuGKEnF+JsIaqvWSsRc9L4KclMxLnNMWfJk8= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.102.0 h1:GTwHSfqebN+NeBwMgptqNratxrm5JzRVva2aw/h3vHQ= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.102.0/go.mod h1:F8FbYLUSWmuqST50OrJGGjywu3Ci5Ssr2cwasYHGfjo= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.102.0 h1:pynCEn05oq47ov80aIiratpdoG/0GytiZ5P3IVQXyeM= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.102.0/go.mod h1:O+IHBEtgSQWLhtuwFzgWo0ztaSBaMeMhOToIV1Ul+W8= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.102.0 h1:PNLVcz8kJLE9V5kGnbBh277Bvl4WwiVZ+NbFbOB80WY= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.102.0/go.mod h1:cBbjwd8m4rBVgCQksUbAVQX1EoM5IuCyNQw2mzvibEM= +github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter v0.104.0 h1:5EXODLMCQ+EG3Nt8llNTr4Q5GGmN9tDl0ZSatZfCk6M= +github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter v0.104.0/go.mod h1:1ApgkABgfYHUKnwXl2JTwM8O5wLcvl8ex7L2NX2vnok= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.104.0 h1:vUbZfZ2K1umomtT378SoL1lnSUzq2ErUGlZi9JJbxuo= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.104.0/go.mod h1:6qaS+Mm7kFLTqOnveY7nP4K994RpAnPkJvirAwOS6EI= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.104.0 h1:t+FXlEYrBzQ9B/YOJKui30g05MF5MrQQ3f0VtNVCSwg= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.104.0/go.mod h1:gSuTmWr1rOh664NHvhxwdkKnk4UTplvJmWCwdojU9Rs= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.104.0 h1:dcs3PHXBShL5+DWmDrNXnESlehQjRjIaVE84GPyZL5E= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.104.0/go.mod h1:Vh707OU/o72qqlDGS+8WVkMCTIlmiTfy3k6PQeq/tgY= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.104.0 h1:4ke4j/y7AQnRAyYveB+KGcdjVYEKVrwTxc3BDHagdd0= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.104.0/go.mod h1:I2zX9YBggIum9LAHXN1DqqbYOENrHXbXdkXouhwVCHw= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver v0.104.0 h1:6fpc2nQDCZ9m2w2lh/EMXoGkCQTOpblkNMWuD/jH7jg= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver v0.104.0/go.mod h1:HMOk2jrjvWeGO3nm3TqLP/fU6x7H30ghOerpQA8atbE= +github.com/open-telemetry/otel-arrow v0.24.0 h1:hNUEbwHW/1gEOUiN+HoI+ITiXe2vSBaPWlE9FRwJwDE= +github.com/open-telemetry/otel-arrow v0.24.0/go.mod h1:uzoHixEh6CUBZkP+vkRvyiHYUnYsAOUwCcfByQkSMM0= +github.com/open-telemetry/otel-arrow/collector v0.24.0 h1:NYTcgtwG0lQnoGcEomTTtueZxzk03xt+XEXN4L5kqHA= +github.com/open-telemetry/otel-arrow/collector v0.24.0/go.mod h1:+jJ3Vfhh685hXSw2Z1P1wl/rTqEKlSaJ4FocZI+xs+0= +github.com/open-telemetry/otel-arrow/collector/connector/validationconnector v0.24.0 h1:6+3Y4eDMBDMJcHkEnqaXLqt5EycDbtCEyeQlSfVCxck= +github.com/open-telemetry/otel-arrow/collector/connector/validationconnector v0.24.0/go.mod h1:5txV07w99k1stJKYHYOSrYa5Ur7VWvh9qVAfGOqfR6I= +github.com/open-telemetry/otel-arrow/collector/exporter/fileexporter v0.24.0 h1:g2OOduQydi7ZIuX8c7BkPWDVAYN2mTGAUa1s4emmy48= +github.com/open-telemetry/otel-arrow/collector/exporter/fileexporter v0.24.0/go.mod h1:5IHrZAjfjjbBRF30hpixr6yB7P2NU8smAXrnQLIJLt8= +github.com/open-telemetry/otel-arrow/collector/processor/concurrentbatchprocessor v0.24.0 h1:zcvNC5p+ByUjAohxWhwYSRTqgWa2LXh0Ev3hLVtNvR0= +github.com/open-telemetry/otel-arrow/collector/processor/concurrentbatchprocessor v0.24.0/go.mod h1:Nd3PpjIyv2DcPhHeU09z1aRh42CdpkxT96amdQi5dmM= +github.com/open-telemetry/otel-arrow/collector/processor/obfuscationprocessor v0.24.0 h1:N2jLd6Mx+Ds9y56pOZL2XTzlNMHIoNzrE1g6csju8mg= +github.com/open-telemetry/otel-arrow/collector/processor/obfuscationprocessor v0.24.0/go.mod h1:NeabvLcfy79b/VsIAYNdBxlX+QvFXFcx1cRGsyLd2m0= +github.com/open-telemetry/otel-arrow/collector/receiver/filereceiver v0.24.0 h1:KRlKK+t/NI6YD+kodBOqs8Tq4JLcfS74MQpC+OJNmRU= +github.com/open-telemetry/otel-arrow/collector/receiver/filereceiver v0.24.0/go.mod h1:aeAOKEqwuj+dR5e+SI2jZNaTziZTM1Lt2wMqIFN8jUg= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -858,8 +878,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= -github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= -github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -868,8 +888,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= -github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/protolambda/bls12-381-util v0.0.0-20220416220906-d8552aa452c7/go.mod h1:IToEjHuttnUzwZI5KBSM/LOOW3qLbbrHOEfp3SbECGY= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= @@ -884,8 +904,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= -github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -904,8 +924,8 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI= -github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= -github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= +github.com/shirou/gopsutil/v4 v4.24.6 h1:9qqCSYF2pgOU+t+NgJtp7Co5+5mHF/HyKBUckySQL64= +github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -936,8 +956,8 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -952,7 +972,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1066,118 +1085,128 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c h1:UmlCWoLNgxxN906BHOXH06/TMeumOrDqdTXeRkYD6d4= -go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:gKjweCX6ve4F7X4RGV3kDN24Bg2eyV6MTCncnaPfRPA= -go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c h1:F17okJGeAtqIZZv/7mZvo6gunwPqdlt40znR0Vo1c1Q= -go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:AM5c/Ohhxj2j/vfCZrwKUD7PrcMpuCbo68rSBibV9U4= -go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c h1:dFcfo09PibmEsdBA2sMLbs+IyBWoPg7NwwEG6eWQRfY= -go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:EJ/PoRVuG1eowA3KQbnW8VonGHGz8jznCXpWG5U8+2A= -go.opentelemetry.io/collector/config/configcompression v1.9.1-0.20240611143128-7dfb57b9ad1c h1:BR/Gtt1BX7psCRIcrw6mIFUUTLfy884WMsQVgoItHeM= -go.opentelemetry.io/collector/config/configcompression v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:6+m0GKCv7JKzaumn7u80A2dLNCuYf5wdR87HWreoBO0= -go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c h1:ZleEsjYf+kxFV5aF8AWHZ4qPFIw/8EQyM2GTnm1ewHo= -go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:Qkn38t0e9y68UlXAWp+S3gsenh09LB9Ct5bJ56inDGQ= -go.opentelemetry.io/collector/config/confighttp v0.102.1 h1:tPw1Xf2PfDdrXoBKLY5Sd4Dh8FNm5i+6DKuky9XraIM= -go.opentelemetry.io/collector/config/confighttp v0.102.1/go.mod h1:k4qscfjxuaDQmcAzioxmPujui9VSgW6oal3WLxp9CzI= -go.opentelemetry.io/collector/config/confignet v0.102.2-0.20240611143128-7dfb57b9ad1c h1:k8bp8JS8b36o3+Pl35cYiSo6pIYV/CW8+etqvRSuoe4= -go.opentelemetry.io/collector/config/confignet v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= -go.opentelemetry.io/collector/config/configopaque v1.9.1-0.20240611143128-7dfb57b9ad1c h1:+OJLmTVoFAzSSYgDW++ltj3ya5ZWjFOlL+sAp3Z4T9U= -go.opentelemetry.io/collector/config/configopaque v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:0xURn2sOy5j4fbaocpEYfM97HPGsiffkkVudSPyTJlM= -go.opentelemetry.io/collector/config/configretry v0.102.2-0.20240611143128-7dfb57b9ad1c h1:1s/JRy/6HKo22PgqsZSRrj3j0KXm0wZPLGZ/rfZlwvU= -go.opentelemetry.io/collector/config/configretry v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:P+RA0IA+QoxnDn4072uyeAk1RIoYiCbxYsjpKX5eFC4= -go.opentelemetry.io/collector/config/configtelemetry v0.102.2-0.20240611143128-7dfb57b9ad1c h1:biIHEgJgIFabkzjRrxyiGs3ZyoJ8jPiJyU8dorKaPWg= -go.opentelemetry.io/collector/config/configtelemetry v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= -go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c h1:Foets1z7XMsh5KwEvGqFhEHem6Kx3xWjfUVUfLk6bF8= -go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:0/mMXy474cvCd4p4VSiZMTaHD/9LwdGbCqXvBPHkDSg= -go.opentelemetry.io/collector/config/internal v0.102.1 h1:HFsFD3xpHUuNHb8/UTz5crJw1cMHzsJQf/86sgD44hw= -go.opentelemetry.io/collector/config/internal v0.102.1/go.mod h1:Vig3dfeJJnuRe1kBNpszBzPoj5eYnR51wXbeq36Zfpg= -go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c h1:LOhGPowRmdpv7HU6HAFkdRvys41RWaijhGmWa7YBOsg= -go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:KgpS7UxH5rkd69CzAzlY2I1heH8Z7eNCZlHmwQBMxNg= -go.opentelemetry.io/collector/confmap/converter/expandconverter v0.102.1 h1:s0RxnaABoRxtfvUeimZ0OOsF83wD/EK1tR2N5GZyst0= -go.opentelemetry.io/collector/confmap/converter/expandconverter v0.102.1/go.mod h1:ZwSMlOSIzmrrSSVNoMPDr21SQx7E52bZFMQJSOZ+EhY= -go.opentelemetry.io/collector/confmap/provider/envprovider v0.102.1 h1:4KLw0pTChIqDfw0ckZ411aQDw98pu2dDOqgBHXfJm8M= -go.opentelemetry.io/collector/confmap/provider/envprovider v0.102.1/go.mod h1:f+IJBW0Sc96T79qj3GQtE1wQ0uWEwpslD785efKBl+c= -go.opentelemetry.io/collector/confmap/provider/fileprovider v0.102.1 h1:nPhOtUbJHfTDqZqtvU76HmEz9iV4O/4/DSCZdnm0mpY= -go.opentelemetry.io/collector/confmap/provider/fileprovider v0.102.1/go.mod h1:eJnr6YDQiocmoRBvsKj33bIc4wysq5hy/jmOApv1dSM= -go.opentelemetry.io/collector/confmap/provider/httpprovider v0.102.1 h1:VsaGXqEUFost0mf2svhds6loYzPavkyY37nMQcqoTkc= -go.opentelemetry.io/collector/confmap/provider/httpprovider v0.102.1/go.mod h1:lQocxKI32Zj1F3PR9UZfzykq50/mOI1mbyZ0729dphI= -go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.102.1 h1:rEhPTqkGAezaFxJ8y/BL5m4vKTK3ZSpn+VcVLKnZo7Q= -go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.102.1/go.mod h1:GxUZM23m3u4vURw/At2zEKW+5GwcuCNsHJNT/Wq/cFI= -go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.102.1 h1:qmdaBIz0UnUKVitZzq+4HtO9zvRTwgNc/Q3b7kyf1NQ= -go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.102.1/go.mod h1:nAckG/FkzAaPuwtEN2Na2+ij+2hdTjtXUtFBnlUqpFk= -go.opentelemetry.io/collector/connector v0.102.2-0.20240612162315-964e3a95872e h1:UgILLcHrU3Xj+i+lN1H+YJBe8kuUNCvS2MFioYj9y5Y= -go.opentelemetry.io/collector/connector v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:6GDuB9stoHIRBPr5Kpbk9ztXBu7U4bMp1nYm56zP1Zs= -go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c h1:L/FPXl2OoOKniPw1hYzCOk6eljlcwCC681y4plDDE08= -go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:4EV8/Rh+KD6z75EjDDWthN50aFeeRqxsC589EpakV5E= -go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e h1:Z5SmlS/YR1O5FGQbXyv0oqNERCDDCMQVUWIIP+0NqM0= -go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:JUUGttX6dnz9+LDR+RzH2Imrr04SKwfpdtq42yknbnQ= -go.opentelemetry.io/collector/exporter/debugexporter v0.102.1 h1:eATGZwBNmqUn8xRr7oGQhoegjeOOCdmtbYgziUoFMf8= -go.opentelemetry.io/collector/exporter/debugexporter v0.102.1/go.mod h1:k5rDZX5pV3DsXZzvI+sk7PKMUljtB7T25PPXAPGBjEs= -go.opentelemetry.io/collector/exporter/otlphttpexporter v0.102.1 h1:9TaxHrkVtEdssDAHqV5yU9PARkFph7CvfLqC1wS6m+c= -go.opentelemetry.io/collector/exporter/otlphttpexporter v0.102.1/go.mod h1:auKlkLfuUriyZ2CmV2dudJaVGB7ycZ+tTpypy2JNFEc= -go.opentelemetry.io/collector/extension v0.102.2-0.20240611143128-7dfb57b9ad1c h1:kDjy3b4gMdXyYbkvJe2ARcfFsnfOsBLth6s7EB2Gp1s= -go.opentelemetry.io/collector/extension v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:UkgI/9uobPWsyKR17PdindQ4+CDL1hbVgpzUgfp9RRg= -go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c h1:+A3fAo4yg/eDswLz67kny4AlMfFWVY3L44IFbCyxZIk= -go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:m7aGTw7yl2Qe5kprqkJeky2wwVKeLYugu6eiS5XcXpY= -go.opentelemetry.io/collector/extension/zpagesextension v0.102.1 h1:YV+ejCgOBJjACOi/l3ULeivOhh85FPE8T4UcFdWviyg= -go.opentelemetry.io/collector/extension/zpagesextension v0.102.1/go.mod h1:/CZXg9/C64k85/k4bc7NFbCNP/MiPUZucbxPUN04ny4= -go.opentelemetry.io/collector/featuregate v1.9.0 h1:mC4/HnR5cx/kkG1RKOQAvHxxg5Ktmd9gpFdttPEXQtA= -go.opentelemetry.io/collector/featuregate v1.9.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= -go.opentelemetry.io/collector/otelcol v0.102.1 h1:JdRG3ven+c5k703QpZG5bxJi4JJOnWaNP/EJvN+oYnI= -go.opentelemetry.io/collector/otelcol v0.102.1/go.mod h1:kHf9KBXOLZXajR1On8XJbBBGcgh2I2+/mVVroPzOLJU= -go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c h1:f8L2r0f684bJAAZDoTvEWccx34C3kQsePNwy8KzTPqM= -go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE= -go.opentelemetry.io/collector/pdata/testdata v0.102.1 h1:S3idZaJxy8M7mCC4PG4EegmtiSaOuh6wXWatKIui8xU= -go.opentelemetry.io/collector/pdata/testdata v0.102.1/go.mod h1:JEoSJTMgeTKyGxoMRy48RMYyhkA5vCCq/abJq9B6vXs= -go.opentelemetry.io/collector/processor v0.102.2-0.20240612162315-964e3a95872e h1:DAw2yXMpPnJ2MLd0mj33DDYWwdxbvEHNB6u24FIGx/g= -go.opentelemetry.io/collector/processor v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:81izr5ORy0YdzmhelV5fRUJvV8ElmeodxToRpL0cocY= -go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e h1:Y1zH80bbNuJWd63T5PjpFs4+NrAl8LvMBxqPCf6XBrM= -go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:A/oJHfYc+1auPv6gbp0B/kR8DLtFIDCB/Z/3nDlHVQs= -go.opentelemetry.io/collector/receiver/otlpreceiver v0.102.1 h1:65/8lkVmOu6gwBw99W+QUQBeDC2qVTwlaiqy7/SpauY= -go.opentelemetry.io/collector/receiver/otlpreceiver v0.102.1/go.mod h1:0hmxfFSSqKJjRGvgYjp/XvptbAgLhLguwNgJqMp7zd0= -go.opentelemetry.io/collector/semconv v0.102.1 h1:zLhz2Gu//j7HHESFTGTrfKIaoS4r+lZFQDnGCOThggo= -go.opentelemetry.io/collector/semconv v0.102.1/go.mod h1:yMVUCNoQPZVq/IPfrHrnntZTWsLf5YGZ7qwKulIl5hw= -go.opentelemetry.io/collector/service v0.102.1 h1:Lg7qrC4Zctd/OAlkpdsaZaUY+jLEGLLnOigfBLP2GW8= -go.opentelemetry.io/collector/service v0.102.1/go.mod h1:L5Sh3461B1Zij7vpMMbi6M/SZicgrLB3UgbG0oUK0pA= -go.opentelemetry.io/contrib/config v0.7.0 h1:b1rK5tGTuhhPirJiMxOcyQfZs76j2VapY6ODn3b2Dbs= -go.opentelemetry.io/contrib/config v0.7.0/go.mod h1:8tdiFd8N5etOi3XzBmAoMxplEzI3TcL8dU5rM5/xcOQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= -go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs0co37SZedQilP2hm0= -go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E= -go.opentelemetry.io/contrib/zpages v0.52.0 h1:MPgkMy0Cp3O5EdfVXP0ss3ujhEibysTM4eszx7E7d+E= -go.opentelemetry.io/contrib/zpages v0.52.0/go.mod h1:fqG5AFdoYru3A3DnhibVuaaEfQV2WKxE7fYE1jgDRwk= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= -go.opentelemetry.io/otel/bridge/opencensus v1.27.0 h1:ao9aGGHd+G4YfjBpGs6vbkvt5hoC67STlJA9fCnOAcs= -go.opentelemetry.io/otel/bridge/opencensus v1.27.0/go.mod h1:uRvWtAAXzyVOST0WMPX5JHGBaAvBws+2F8PcC5gMnTk= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= -go.opentelemetry.io/otel/exporters/prometheus v0.49.0 h1:Er5I1g/YhfYv9Affk9nJLfH/+qCCVVg1f2R9AbJfqDQ= -go.opentelemetry.io/otel/exporters/prometheus v0.49.0/go.mod h1:KfQ1wpjf3zsHjzP149P4LyAwWRupc6c7t1ZJ9eXpKQM= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 h1:/jlt1Y8gXWiHG9FBx6cJaIC5hYx5Fe64nC8w5Cylt/0= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0/go.mod h1:bmToOGOBZ4hA9ghphIc1PAf66VA8KOtsuy3+ScStG20= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 h1:/0YaXu3755A/cFbtXp+21lkXgI0QE5avTWA2HjU9/WE= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0/go.mod h1:m7SFxp0/7IxmJPLIY3JhOcU9CoFzDaCPL6xxQIxhA+o= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= -go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= -go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= -go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= -go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= +go.opentelemetry.io/collector v0.105.0 h1:Qw/ONVMPT3aD8HjdDRcXCGoZrtSWH3jx4BkwAN1yrEM= +go.opentelemetry.io/collector v0.105.0/go.mod h1:UVapTqB4fJeZpGU/YgOo6665cxCSytqYmMkVmRlu2cg= +go.opentelemetry.io/collector/component v0.105.0 h1:/OdkWHd1xTNX7JRq9iW3AFoJAnYUOGZZyOprNQkGoTI= +go.opentelemetry.io/collector/component v0.105.0/go.mod h1:s8KoxOrhNIBzetkb0LHmzX1OI67DyZbaaUPOWIXS1mg= +go.opentelemetry.io/collector/config/configauth v0.105.0 h1:9Pa65Ay4kdmMsp5mg+/791GvCYy1hHOroIlKBiJzlps= +go.opentelemetry.io/collector/config/configauth v0.105.0/go.mod h1:iL62YzyFCNr1Se0EDYaQ792CFCBiFivSbTWekd4g1VE= +go.opentelemetry.io/collector/config/configcompression v1.12.0 h1:RxqSDVZPJyL7I3v+gdVDvnJ/9tV0ZWgraRDX/gaddfA= +go.opentelemetry.io/collector/config/configcompression v1.12.0/go.mod h1:6+m0GKCv7JKzaumn7u80A2dLNCuYf5wdR87HWreoBO0= +go.opentelemetry.io/collector/config/configgrpc v0.105.0 h1:3IbN6+5c42Bp6CGPKoXZ7QkkQpRxnV01KRse6oD+bd0= +go.opentelemetry.io/collector/config/configgrpc v0.105.0/go.mod h1:wrFPXVXk4so7yYisuPeebZtU6ECzG5aXOYNdfyoHSnI= +go.opentelemetry.io/collector/config/confighttp v0.105.0 h1:0sVdNFuLikgDhxdp+mrbSrYzovwRxZDVIhv9ZQ0X5e4= +go.opentelemetry.io/collector/config/confighttp v0.105.0/go.mod h1:1JMvF8ocjlNxwrVIUJHy9wTEW/nlKs1LLPlTSg4D044= +go.opentelemetry.io/collector/config/confignet v0.105.0 h1:O8kenkWnLPemp2XXVOqFv6OQd9wsnOvBUvl3OlJGPKI= +go.opentelemetry.io/collector/config/confignet v0.105.0/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= +go.opentelemetry.io/collector/config/configopaque v1.12.0 h1:aIsp9NdcLZSiG4YDoFPGXhmma03Tk+6e89+n8GtU/Mc= +go.opentelemetry.io/collector/config/configopaque v1.12.0/go.mod h1:0xURn2sOy5j4fbaocpEYfM97HPGsiffkkVudSPyTJlM= +go.opentelemetry.io/collector/config/configretry v1.12.0 h1:tEBwueO4AIkwWosxz6NWqnghdZ7y5SfHcIzLrvh6kB8= +go.opentelemetry.io/collector/config/configretry v1.12.0/go.mod h1:P+RA0IA+QoxnDn4072uyeAk1RIoYiCbxYsjpKX5eFC4= +go.opentelemetry.io/collector/config/configtelemetry v0.105.0 h1:wEfUxAjjstp47aLr2s1cMZiH0dt+k42m6VC6HigqgJA= +go.opentelemetry.io/collector/config/configtelemetry v0.105.0/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= +go.opentelemetry.io/collector/config/configtls v1.12.0 h1:Px0+GE4LE/9sXMgkwBb5g8QHWvnrnuRg9BLSa+QtxgM= +go.opentelemetry.io/collector/config/configtls v1.12.0/go.mod h1:aeCGPlvrWhc+EySpIKdelPAj4l9wXKzZPouQO3NIoTs= +go.opentelemetry.io/collector/config/internal v0.105.0 h1:PWnbeslkIGMjZzh5IJRjO6bA02d1Xrkjw2N60ixWzqQ= +go.opentelemetry.io/collector/config/internal v0.105.0/go.mod h1:+Y5vRJ+lio2uuYlVPfy9AZVrip9Y0B9PiUA5Vz7lzZw= +go.opentelemetry.io/collector/confmap v0.105.0 h1:3NP2BbUju42rjeQvRbmpCJGJGvbiV3WnGyXsVmocimo= +go.opentelemetry.io/collector/confmap v0.105.0/go.mod h1:Oj1xUBRvAuL8OWWMj9sSYf1uQpB+AErpj+FKGUQLBI0= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.105.0 h1:1c81AJg+WP+dGoAEftmc0KF5RK0mh4bxIFlU902CnFI= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.105.0/go.mod h1:ImkfsdJToeP3ZZ5ZVmQUdaKEueVHiZbwrQGyoDx/EiM= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.105.0 h1:r+5ZxhDLYqd6klLky3gtgWHgG5L2LX6o6VIvaLG39Gs= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.105.0/go.mod h1:Srzkp9izW29pJtwoVtpL+bAN5ibtv+fyLg9JWZzCCi8= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.105.0 h1:RgQwEbJgx+BihDt8yhiqmEi8IjQYFwbzsYRcTzvM7E4= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.105.0/go.mod h1:P7oT+6eaMv+ZyPSu6SEKalfR/UALdQgE3hq36b0Iww4= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.105.0 h1:hgzauRlEZId1AM00g3EHl4GaR4GLG23sdvmSRWQ9iFQ= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.105.0/go.mod h1:TqZbmLcG9GvLp76q45eE0EwiTHBmj4qRP32fI48DH5Q= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.105.0 h1:xQr2I7iQPDkFP/E2H9lg6jfAtpY1iz2oLGEFwa2FaeE= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.105.0/go.mod h1:UVTcdI8WhTXl8duc0EkeJU7nSQNptHwE6D/eqgeand0= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.105.0 h1:SAF6JFrjQ8YkFnggA5iiw9eKiBYDaO6ynK0Dl+vzIgw= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.105.0/go.mod h1:bWMg+SNk/PwEMiLWV0eCFNL4Er59aXsdDKq+2JzMOkE= +go.opentelemetry.io/collector/connector v0.105.0 h1:hmTsI6rnPAXsdVJ+wYrgeNTCrQzxzglU37qPlNSKTy0= +go.opentelemetry.io/collector/connector v0.105.0/go.mod h1:xQnq1Ygd/F4Kb93E0UsKus5YDINeyQ6GzCkxoujI9TE= +go.opentelemetry.io/collector/consumer v0.105.0 h1:pO5Tspoz7yvEs81+904HfDjByP8Z7uuNk+7pOr3lRHM= +go.opentelemetry.io/collector/consumer v0.105.0/go.mod h1:tnaPDHUfKBJ01OnsJNRecniG9iciE+xHYLqamYwFQOQ= +go.opentelemetry.io/collector/exporter v0.105.0 h1:O2xmjfaRbkbpo3XkwEcnuBHCoXc5kS9CjYO8geu+3vo= +go.opentelemetry.io/collector/exporter v0.105.0/go.mod h1:5ulGEHRZyGbX4DWHJa2Br6Fr/W1Lay8ayf++1WrVvgk= +go.opentelemetry.io/collector/exporter/debugexporter v0.105.0 h1:Osqo7+pr1vZbt+12KMzZrst6uMv1r8UJZjTgI6vwqL4= +go.opentelemetry.io/collector/exporter/debugexporter v0.105.0/go.mod h1:rqjWoduh/qj6OlttzdbB8Ji6Vp4SoHJRnZHNuS/Mkp0= +go.opentelemetry.io/collector/exporter/otlphttpexporter v0.105.0 h1:ciJvNlC9ztoeyIi23TCpZ6PN7l75tl/yvKqBo0Cmajw= +go.opentelemetry.io/collector/exporter/otlphttpexporter v0.105.0/go.mod h1:e+PdCkQkP+1cj0+cijRfmE4/VjofxeeWoS1cvvu39HI= +go.opentelemetry.io/collector/extension v0.105.0 h1:R8i4HMvuSm20Nt3onyrLk19KKhjCNAsgS8FGh60rcZU= +go.opentelemetry.io/collector/extension v0.105.0/go.mod h1:oyX960URG27esNKitf3o2rqcBj0ajcx+dxkCxwRz34U= +go.opentelemetry.io/collector/extension/auth v0.105.0 h1:5gzRSHU0obVtZDzLLJQ/p4sIkacUsyEEpBiBRDs82Hk= +go.opentelemetry.io/collector/extension/auth v0.105.0/go.mod h1:zf45v7u1nKbdDHeMuhBVdSFwhbq2w9IWCbFKcDSkW5I= +go.opentelemetry.io/collector/extension/zpagesextension v0.105.0 h1:rD77+hMPaSuBOh8w6un+x0VI5oM2oAsSzAC9nKTNauE= +go.opentelemetry.io/collector/extension/zpagesextension v0.105.0/go.mod h1:vDuL/PBfZfHpOyF2XCJ/jWfHn/hu2pCE5KVRGCHOX+k= +go.opentelemetry.io/collector/featuregate v1.12.0 h1:l5WbV2vMQd2bL8ubfGrbKNtZaeJRckE12CTHvRe47Tw= +go.opentelemetry.io/collector/featuregate v1.12.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= +go.opentelemetry.io/collector/internal/globalgates v0.105.0 h1:U/CwnTUXtrblD1sZ6ri7KWfYoTNjQd7GjJKrX/phRik= +go.opentelemetry.io/collector/internal/globalgates v0.105.0/go.mod h1:Z5US6O2xkZAtxVSSBnHAPFZwPhFoxlyKLUvS67Vx4gc= +go.opentelemetry.io/collector/otelcol v0.105.0 h1:2YMp/OckEc4qEfIjx3pi7W7xr+nW03KdrdhHAt3NmYY= +go.opentelemetry.io/collector/otelcol v0.105.0/go.mod h1:SwdiOY/0szbyuJ1sDDv66W5nFPVkA9hOhQoEBAFbHA0= +go.opentelemetry.io/collector/pdata v1.12.0 h1:Xx5VK1p4VO0md8MWm2icwC1MnJ7f8EimKItMWw46BmA= +go.opentelemetry.io/collector/pdata v1.12.0/go.mod h1:MYeB0MmMAxeM0hstCFrCqWLzdyeYySim2dG6pDT6nYI= +go.opentelemetry.io/collector/pdata/pprofile v0.105.0 h1:C+Hd7CNcepL/364OBV9f4lHzJil2jQSOxcEM1PFXGDg= +go.opentelemetry.io/collector/pdata/pprofile v0.105.0/go.mod h1:chr7lMJIzyXkccnPRkIPhyXtqLZLSReZYhwsggOGEfg= +go.opentelemetry.io/collector/pdata/testdata v0.105.0 h1:5sPZzanR4nJR3sNQk3MTdArdEZCK0NRAfC29t0Dtf60= +go.opentelemetry.io/collector/pdata/testdata v0.105.0/go.mod h1:NIfgaclQp/M1BZhgyc/7hDWD+/DumC/OMBQVI2KW+N0= +go.opentelemetry.io/collector/processor v0.105.0 h1:LE6wEMWNa3h7eOJLMBm2lA0v6sc2j6Geqv1e3pIWS8Y= +go.opentelemetry.io/collector/processor v0.105.0/go.mod h1:QAvMEtd3k+YhRrnaEgs/8e0UueYonuT8Hg4udQOht60= +go.opentelemetry.io/collector/receiver v0.105.0 h1:eZF97kMUnKJ20Uc4PaDlgLIGmaA8kyLqhH+vMXjh92U= +go.opentelemetry.io/collector/receiver v0.105.0/go.mod h1:nGKDXLUGVHxMBJ5QLfsJ/bIhGvoMGqsN0pZtD5SC8sE= +go.opentelemetry.io/collector/receiver/otlpreceiver v0.105.0 h1:1qjnvrnDcEaj93WgyrWRWTTAGNODaxi98wNcXiHDwfM= +go.opentelemetry.io/collector/receiver/otlpreceiver v0.105.0/go.mod h1:XGs9OAQQFDfVhSzruLcFNdzhTw2kbZZAV68iRE9ecGA= +go.opentelemetry.io/collector/semconv v0.105.0 h1:8p6dZ3JfxFTjbY38d8xlQGB1TQ3nPUvs+D0RERniZ1g= +go.opentelemetry.io/collector/semconv v0.105.0/go.mod h1:yMVUCNoQPZVq/IPfrHrnntZTWsLf5YGZ7qwKulIl5hw= +go.opentelemetry.io/collector/service v0.105.0 h1:93dqJVNKToEvYDYZO2pHrN8f6Uev4+xhzzmw4qwgAzE= +go.opentelemetry.io/collector/service v0.105.0/go.mod h1:H0fD5SZjV2AZTgHEjRhObnP8YrhG2etAh9rsWLtmimQ= +go.opentelemetry.io/contrib/config v0.8.0 h1:OD7aDMhL+2EpzdSHfkDmcdD/uUA+PgKM5faFyF9XFT0= +go.opentelemetry.io/contrib/config v0.8.0/go.mod h1:dGeVZWE//3wrxYHHP0iCBYJU1QmOmPcbV+FNB7pjDYI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/contrib/propagators/b3 v1.28.0 h1:XR6CFQrQ/ttAYmTBX2loUEFGdk1h17pxYI8828dk/1Y= +go.opentelemetry.io/contrib/propagators/b3 v1.28.0/go.mod h1:DWRkzJONLquRz7OJPh2rRbZ7MugQj62rk7g6HRnEqh0= +go.opentelemetry.io/contrib/zpages v0.53.0 h1:hGgaJ3nrescxEk383gOBHA5gNfoquHs8oV/XcKYxJkw= +go.opentelemetry.io/contrib/zpages v0.53.0/go.mod h1:iOo8fpUxMAu5+4x9DSEQeUOCeY19KaN6v2OPSeIggz4= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/bridge/opencensus v1.28.0 h1:/BcyAV1bUJjSVxoeKwTQL9cS4X1iC6izZ9mheeuVSCU= +go.opentelemetry.io/otel/bridge/opencensus v1.28.0/go.mod h1:FZp2xE+46yAyp3DfLFALze58nY0iIE8zs+mCgkPAzq0= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 h1:zBPZAISA9NOc5cE8zydqDiS0itvg/P/0Hn9m72a5gvM= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0/go.mod h1:gcj2fFjEsqpV3fXuzAA+0Ze1p2/4MJ4T7d77AmkvueQ= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0/go.mod h1:yeGZANgEcpdx/WK0IvvRFC+2oLiMS2u4L/0Rj2M2Qr0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 h1:aLmmtjRke7LPDQ3lvpFz+kNEH43faFhzW7v8BFIEydg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0/go.mod h1:TC1pyCt6G9Sjb4bQpShH+P5R53pO6ZuGnHuuln9xMeE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk= +go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng= +go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 h1:BJee2iLkfRfl9lc7aFmBwkWxY/RI1RDdXepSF6y8TPE= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0/go.mod h1:DIzlHs3DRscCIBU3Y9YSzPfScwnYnzfnCd4g8zA7bZc= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y= +go.opentelemetry.io/otel/log v0.4.0 h1:/vZ+3Utqh18e8TPjuc3ecg284078KWrR8BRz+PQAj3o= +go.opentelemetry.io/otel/log v0.4.0/go.mod h1:DhGnQvky7pHy82MIRV43iXh3FlKN8UUKftn0KbLOq6I= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk/log v0.4.0 h1:1mMI22L82zLqf6KtkjrRy5BbagOTWdJsqMY/HSqILAA= +go.opentelemetry.io/otel/sdk/log v0.4.0/go.mod h1:AYJ9FVF0hNOgAVzUG/ybg/QttnXhUePWAupmCqtdESo= +go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= +go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -1228,8 +1257,8 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0 golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1363,8 +1392,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20170207211851-4464e7848382/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1509,9 +1538,8 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1541,8 +1569,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1620,8 +1648,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1716,10 +1744,10 @@ google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 h1:Q2RxlXqh1cgzzUgV261vBO2jI5R/3DD1J2pM0nI4NhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v0.0.0-20170208002647-2a6bf6142e96/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1743,8 +1771,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/collector/exporter/otelarrowexporter/Makefile b/collector/exporter/otelarrowexporter/Makefile deleted file mode 100644 index ded7a360..00000000 --- a/collector/exporter/otelarrowexporter/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../../Makefile.Common diff --git a/collector/exporter/otelarrowexporter/README.md b/collector/exporter/otelarrowexporter/README.md deleted file mode 100644 index 43b084e3..00000000 --- a/collector/exporter/otelarrowexporter/README.md +++ /dev/null @@ -1,250 +0,0 @@ -# OpenTelemetry Protocol with Apache Arrow Exporter - - -| Status | | -| ------------- |-----------| -| Stability | [development]: traces, metrics, logs | -| Distributions | [] | -| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aexporter%2Fotelarrow%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aexporter%2Fotelarrow) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aexporter%2Fotelarrow%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aexporter%2Fotelarrow) | -| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@jmacd](https://www.github.com/jmacd), [@moh-osman3](https://www.github.com/moh-osman3) | - -[development]: https://github.com/open-telemetry/opentelemetry-collector#development - - -Exports telemetry data using [OpenTelemetry Protocol with Apache -Arrow](https://github.com/open-telemetry/otel-arrow) components with -support for both OpenTelemetry Protocol with Apache -Arrow and standard [OpenTelemetry Protocol]( -https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md) -(OTLP) protocol via gRPC. - -## Getting Started - -The [OpenTelemetry Protocol with Apache -Arrow](https://github.com/open-telemetry/otel-arrow) exporter combines -the features and configuration syntax of the core OpenTelemetry -Collector [OTLP -exporter](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/otlpexporter) -component with additional support for the OpenTelemetry Protocol with -Apache Arrow. - -OpenTelemetry Protocol with Apache Arrow supports column-oriented data -transport using the Apache Arrow data format. This component converts -OTLP data into an optimized representation and then sends batches of -data using Apache Arrow to encode the stream. The [OpenTelemetry -Protocol with Apache Arrow receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/otelarrowreceiver) component contains logic to reverse the process used in this -component. - -The use of an OpenTelemetry Protocol with Apache Arrow -exporter-receiver pair is recommended when the network is expensive. -Typically, expect to see a 50% reduction in bandwidth compared with -the same data being sent using standard OTLP/gRPC with Zstd -compression, batch sizes being equal. - -This component includes all the features and configuration of the core -OTLP exporter, making it possible to upgrade from the core OTLP -exporter component. This is as simple as replacing "otlp" with -"otelarrow" as the component name in the collector configuration. - -To enable the OpenTelemetry Protocol with Apache Arrow exporter, -include it in the list of exporters for a pipeline. The `endpoint` -setting is required. The `tls` setting is required for insecure -transport. - -- `endpoint` (no default): host:port to which the exporter is going to send OTLP trace data, -using the gRPC protocol. The valid syntax is described -[here](https://github.com/grpc/grpc/blob/master/doc/naming.md). -If a scheme of `https` is used then client transport security is enabled and overrides the `insecure` setting. -- `tls`: see [TLS Configuration Settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configtls/README.md) for the full set of available options. - -Example: - -```yaml -exporters: - otelarrow/secure: - endpoint: external-collector:4317 - tls: - cert_file: file.cert - key_file: file.key - otelarrow/insecure: - endpoint: internal-collector:4317 - tls: - insecure: true -``` - -By default, `zstd` compression is enabled at the gRPC level. See -[compression configuration](#compression-configuration) below. To -disable gRPC-level compression, configure "none": - -```yaml -exporters: - otelarrow: - compression: none - endpoint: ... - tls: ... -``` - -## Configuration - -Several helper files are leveraged to provide additional capabilities automatically: - -- [gRPC settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configgrpc/README.md) -- [TLS and mTLS settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configtls/README.md) -- [Queuing, retry and timeout settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/exporterhelper/README.md) - -### Arrow-specific Configuration - -In the `arrow` configuration block, the following settings enable and -disable the use of OpenTelemetry Protocol with Apache Arrow as opposed -to standard OTLP. - -- `disabled` (default: false): disables use of Arrow, causing the exporter to use standard OTLP -- `disable_downgrade` (default: false): prevents this exporter from using standard OTLP. - -The following settings determine the resources that the exporter will use: - -- `num_streams` (default: number of CPUs): the number of concurrent Arrow streams -- `max_stream_lifetime` (default: unlimited): duration after which streams are recycled. - -#### Load balancing - -The `arrow` configuration block includes a configurable prioritization -policy. The default policy, named "fifo", distributes work to the -stream with the first-available writer. - -- `prioritizer` (default: fifo): may be set to "leastloadedN" with N a positive integer. Values of N greater than `num_streams` are lowered to equal `num_streams`. - -An alternative policy named "leastloadedN" is available, for values of -N up to the number of configured streams (e.g., leastloaded2, -leastloaded10). This prioritizer makes a random selection of up to N -streams and chooses the stream with the least number of outstanding -work items. - -### Network Configuration - -This component uses `round_robin` by default as the gRPC load -balancer. This can be modified using the `balancer_name` setting, for -example, to configure the `pick_first` balancer: - -```yaml -exporters: - otelarrow: - balancer_name: pick_first - endpoint: ... - tls: ... -``` - -When the server or an intermediate proxy uses a keepalive setting, the -Arrow-specific `max_stream_lifetime` setting is critical to avoiding -abrupt termination of Arrow streams, which causes retries of the -in-flight requests. The maximum stream lifetime should be set to a -value less than the minimum of the server's keepalive parameter (and -any of the intermediate proxies), plus the export timeout. - -```yaml -exporters: - otelarrow: - timeout: 30s - arrow: - max_stream_lifetime: 9m30s - endpoint: ... - tls: ... -``` - -When this is configured, the stream will terminate cleanly without -causing retries, with `OK` gRPC status. - -The [corresponding `otelarrowreceiver` keepalive setting](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/otelarrowreceiver#keepalive-configuration), that is -compatible with the one above, reads: - -``` -receivers: - otelarrow: - protocols: - grpc: - keepalive: - server_parameters: - max_connection_age: 1m - max_connection_age_grace: 10m -``` - -### Exporter metrics - -In addition to the the standard -[exporterhelper](https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/exporterhelper/README.md) -and -[obsreport](https://pkg.go.dev/go.opentelemetry.io/collector/obsreport) -metrics, this component provides network-level measurement instruments -which we anticipate will become part of `exporterhelper` and/or -`obsreport` in the future. At the `normal` level of metrics detail: - -- `exporter_sent`: uncompressed bytes sent, prior to compression -- `exporter_sent_wire`: compressed bytes sent, on the wire. - -Arrow's compression performance can be derived by dividing the average -`exporter_sent` value by the average `exporter_sent_wire` value. - -At the `detailed` metrics detail level, information about the stream -of data being returned to the exporter will be instrumented: - -- `exporter_recv`: uncompressed bytes received, prior to compression -- `exporter_recv_wire`: compressed bytes received, on the wire. - -### Compression Configuration - -The exporter supports configuring Zstd compression at both the gRPC -and the Arrow level. The exporter metrics described above will be -correct in either case. The default settings are subject to change as -we gain experience. - -See the Collector [compression -comparison](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configgrpc/README.md#compression-comparison) -for general information about the choice of Zstd by default, for other -general compression configuration and benchmark information. - -For the OpenTelemetry Protocol with Apache Arrow streams specifically, -gRPC-level the Zstd compression level can be configured. However, -there is an important caveat: the gRPC-Go library requires that -compressor implementations be registered statically. These libraries -use compressors named `zstdarrow1`, `zstdarrow2`, ..., `zstdarrow10`, -supporting 10 configurable compression levels. Note, however that -these configurations are static and only one unique configuration is -possible per level. It is possible to configure multiple -OpenTelemetry Protocol with Apache Arrow exporters with different Zstd -configuration simply by using distinct levels. - -Under `arrow`, the `zstd` sub-configuration has the following fields: - -- `level`: in the range 1-10 determines a number of defaults (default 5) -- `window_size_mib`: size of the Zstd window in MiB, 0 indicates to determine based on level (default 0) -- `concurrency`: controls background CPU used for compression, 0 indicates to let `zstd` library decide (default 1) - -The exporter supports configuring compression at the [Arrow -columnar-protocol -level](https://arrow.apache.org/docs/format/Columnar.html#format-ipc). - -- `payload_compression`: compression applied at the Arrow IPC level, "none" by default, "zstd" supported. - - payload_compression: zstd # describes Arrow-IPC compression (default "none") - -Compression settings at the Arrow IPC level cannot be further -configured. We do not recommend configuring both payload and -gRPC-level compression at once, hwoever these settings are -independent. - -For example, two exporters may be configured with multiple zstd -configurations, provided they use different levels: - -```yaml -exporters: - otelarrow/best: - compression: zstd # describes gRPC-level compression (default "zstd") - arrow: - zstd: - level: 10 # describes gRPC-level compression level (default 5) - otelarrow/fastest: - compression: zstd - arrow: - zstd: - level: 1 # 1 is the "fastest" compression level -``` diff --git a/collector/exporter/otelarrowexporter/config.go b/collector/exporter/otelarrowexporter/config.go deleted file mode 100644 index af411802..00000000 --- a/collector/exporter/otelarrowexporter/config.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowexporter // import "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter" - -import ( - "fmt" - "time" - - "github.com/open-telemetry/otel-arrow/collector/compression/zstd" - "github.com/open-telemetry/otel-arrow/pkg/config" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configcompression" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/config/configretry" - "go.opentelemetry.io/collector/exporter/exporterhelper" - "google.golang.org/grpc" - - "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow" -) - -// Config defines configuration for OTLP exporter. -type Config struct { - // Timeout, Retry, Queue, and gRPC client settings are - // inherited from exporterhelper using field names - // intentionally identical to the core OTLP exporter. - - exporterhelper.TimeoutSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct. - exporterhelper.QueueSettings `mapstructure:"sending_queue"` - - RetryConfig configretry.BackOffConfig `mapstructure:"retry_on_failure"` - - configgrpc.ClientConfig `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct. - - // Arrow includes settings specific to OTel Arrow. - Arrow ArrowConfig `mapstructure:"arrow"` - - // UserDialOptions cannot be configured via `mapstructure` - // schemes. This is useful for custom purposes where the - // exporter is built and configured via code instead of yaml. - // Uses include custom dialer, custom user-agent, etc. - UserDialOptions []grpc.DialOption `mapstructure:"-"` -} - -// ArrowConfig includes whether Arrow is enabled and the number of -// concurrent Arrow streams. -type ArrowConfig struct { - // NumStreams determines the number of OTel Arrow streams. - NumStreams int `mapstructure:"num_streams"` - - // MaxStreamLifetime should be set to less than the value of - // grpc: keepalive: max_connection_age_grace plus the timeout. - MaxStreamLifetime time.Duration `mapstructure:"max_stream_lifetime"` - - // Zstd settings apply to OTel-Arrow use of gRPC specifically. - // Note that when multiple Otel-Arrow exporters are configured - // their settings will be applied in arbitrary order. - // Identical Zstd settings are recommended when multiple - // OTel-Arrow exporters are in use. - Zstd zstd.EncoderConfig `mapstructure:"zstd"` - - // PayloadCompression is applied on the Arrow IPC stream - // internally and may have different results from using - // gRPC-level compression. This is disabled by default, since - // gRPC-level compression is enabled by default. This can be - // set to "zstd" to turn on Arrow-Zstd compression. - // Note that `Zstd` applies to gRPC, not Arrow compression. - PayloadCompression configcompression.Type `mapstructure:"payload_compression"` - - // Disabled prevents using OTel-Arrow streams. The exporter - // falls back to standard OTLP. - Disabled bool `mapstructure:"disabled"` - - // DisableDowngrade prevents this exporter from fallback back - // to standard OTLP. If the Arrow service is unavailable, it - // will retry and/or fail. - DisableDowngrade bool `mapstructure:"disable_downgrade"` - - // Prioritizer is a policy name for how load is distributed - // across streams. - Prioritizer arrow.PrioritizerName `mapstructure:"prioritizer"` -} - -var _ component.Config = (*Config)(nil) - -var _ component.ConfigValidator = (*ArrowConfig)(nil) - -// Validate returns an error when the number of streams is less than 1. -func (cfg *ArrowConfig) Validate() error { - if cfg.NumStreams < 1 { - return fmt.Errorf("stream count must be > 0: %d", cfg.NumStreams) - } - - if cfg.MaxStreamLifetime.Seconds() < 1 { - return fmt.Errorf("max stream life must be >= 1s: %d", cfg.MaxStreamLifetime) - } - - if err := cfg.Zstd.Validate(); err != nil { - return fmt.Errorf("zstd encoder: invalid configuration: %w", err) - } - - if err := cfg.Prioritizer.Validate(); err != nil { - return fmt.Errorf("invalid prioritizer: %w", err) - } - - // The cfg.PayloadCompression field is validated by the underlying library, - // but we only support Zstd or none. - switch cfg.PayloadCompression { - case "none", "", configcompression.TypeZstd: - default: - return fmt.Errorf("unsupported payload compression: %s", cfg.PayloadCompression) - } - return nil -} - -func (cfg *ArrowConfig) toArrowProducerOptions() (arrowOpts []config.Option) { - switch cfg.PayloadCompression { - case configcompression.TypeZstd: - arrowOpts = append(arrowOpts, config.WithZstd()) - case "none", "": - arrowOpts = append(arrowOpts, config.WithNoZstd()) - default: - // Should have failed in validate, nothing we can do. - } - return -} diff --git a/collector/exporter/otelarrowexporter/config_test.go b/collector/exporter/otelarrowexporter/config_test.go deleted file mode 100644 index 253d579e..00000000 --- a/collector/exporter/otelarrowexporter/config_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowexporter - -import ( - "math" - "path/filepath" - "testing" - "time" - - "github.com/open-telemetry/otel-arrow/collector/compression/zstd" - "github.com/open-telemetry/otel-arrow/pkg/config" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configauth" - "go.opentelemetry.io/collector/config/configcompression" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/config/configopaque" - "go.opentelemetry.io/collector/config/configretry" - "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/confmap/confmaptest" - "go.opentelemetry.io/collector/exporter/exporterhelper" - - "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow" -) - -func TestUnmarshalDefaultConfig(t *testing.T) { - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "default.yaml")) - require.NoError(t, err) - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.NoError(t, cm.Unmarshal(cfg)) - assert.Equal(t, factory.CreateDefaultConfig(), cfg) - assert.Equal(t, "round_robin", cfg.(*Config).ClientConfig.BalancerName) - assert.Equal(t, arrow.DefaultPrioritizer, cfg.(*Config).Arrow.Prioritizer) -} - -func TestUnmarshalConfig(t *testing.T) { - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) - require.NoError(t, err) - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.NoError(t, cm.Unmarshal(cfg)) - assert.Equal(t, - &Config{ - TimeoutSettings: exporterhelper.TimeoutSettings{ - Timeout: 10 * time.Second, - }, - RetryConfig: configretry.BackOffConfig{ - Enabled: true, - InitialInterval: 10 * time.Second, - RandomizationFactor: 0.7, - Multiplier: 1.3, - MaxInterval: 1 * time.Minute, - MaxElapsedTime: 10 * time.Minute, - }, - QueueSettings: exporterhelper.QueueSettings{ - Enabled: true, - NumConsumers: 2, - QueueSize: 10, - }, - ClientConfig: configgrpc.ClientConfig{ - Headers: map[string]configopaque.String{ - "can you have a . here?": "F0000000-0000-0000-0000-000000000000", - "header1": "234", - "another": "somevalue", - }, - Endpoint: "1.2.3.4:1234", - Compression: "none", - TLSSetting: configtls.ClientConfig{ - Config: configtls.Config{ - CAFile: "/var/lib/mycert.pem", - }, - Insecure: false, - }, - Keepalive: &configgrpc.KeepaliveClientConfig{ - Time: 20 * time.Second, - PermitWithoutStream: true, - Timeout: 30 * time.Second, - }, - WriteBufferSize: 512 * 1024, - BalancerName: "experimental", - Auth: &configauth.Authentication{AuthenticatorID: component.NewID(component.MustNewType("nop"))}, - }, - Arrow: ArrowConfig{ - NumStreams: 2, - MaxStreamLifetime: 2 * time.Hour, - PayloadCompression: configcompression.TypeZstd, - Zstd: zstd.DefaultEncoderConfig(), - Prioritizer: "leastloaded8", - }, - }, cfg) -} - -func TestArrowConfigValidate(t *testing.T) { - settings := func(enabled bool, numStreams int, maxStreamLifetime time.Duration, level zstd.Level) *ArrowConfig { - return &ArrowConfig{ - Disabled: !enabled, - NumStreams: numStreams, - MaxStreamLifetime: maxStreamLifetime, - Zstd: zstd.EncoderConfig{ - Level: level, - }, - } - } - require.NoError(t, settings(true, 1, 10*time.Second, zstd.DefaultLevel).Validate()) - require.NoError(t, settings(false, 1, 10*time.Second, zstd.DefaultLevel).Validate()) - require.NoError(t, settings(true, 2, 1*time.Second, zstd.DefaultLevel).Validate()) - require.NoError(t, settings(true, math.MaxInt, 10*time.Second, zstd.DefaultLevel).Validate()) - require.NoError(t, settings(true, math.MaxInt, 10*time.Second, zstd.MaxLevel).Validate()) - require.NoError(t, settings(true, math.MaxInt, 10*time.Second, zstd.MinLevel).Validate()) - - require.Error(t, settings(true, 0, 10*time.Second, zstd.DefaultLevel).Validate()) - require.Contains(t, settings(true, 0, 10*time.Second, zstd.DefaultLevel).Validate().Error(), "stream count must be") - require.Contains(t, settings(true, 1, -1*time.Second, zstd.DefaultLevel).Validate().Error(), "max stream life must be") - require.Error(t, settings(false, -1, 10*time.Second, zstd.DefaultLevel).Validate()) - require.Error(t, settings(false, 1, -1*time.Second, zstd.DefaultLevel).Validate()) - require.Error(t, settings(true, math.MinInt, 10*time.Second, zstd.DefaultLevel).Validate()) - require.Error(t, settings(true, math.MaxInt, 10*time.Second, zstd.MinLevel-1).Validate()) - require.Error(t, settings(true, math.MaxInt, 10*time.Second, zstd.MaxLevel+1).Validate()) -} - -func TestDefaultConfigValid(t *testing.T) { - cfg := createDefaultConfig() - // this must be set by the user and config - // validation always checks that a value is set. - cfg.(*Config).Arrow.MaxStreamLifetime = 2 * time.Second - require.NoError(t, component.ValidateConfig(cfg)) -} - -func TestArrowConfigPayloadCompressionZstd(t *testing.T) { - settings := ArrowConfig{ - PayloadCompression: configcompression.TypeZstd, - } - var config config.Config - for _, opt := range settings.toArrowProducerOptions() { - opt(&config) - } - require.True(t, config.Zstd) -} - -func TestArrowConfigPayloadCompressionNone(t *testing.T) { - for _, value := range []string{"", "none"} { - settings := ArrowConfig{ - PayloadCompression: configcompression.Type(value), - } - var config config.Config - for _, opt := range settings.toArrowProducerOptions() { - opt(&config) - } - require.False(t, config.Zstd) - } -} diff --git a/collector/exporter/otelarrowexporter/doc.go b/collector/exporter/otelarrowexporter/doc.go deleted file mode 100644 index 64632b5c..00000000 --- a/collector/exporter/otelarrowexporter/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -//go:generate mdatagen metadata.yaml - -package otelarrowexporter // import "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter" diff --git a/collector/exporter/otelarrowexporter/factory.go b/collector/exporter/otelarrowexporter/factory.go deleted file mode 100644 index 12fe9397..00000000 --- a/collector/exporter/otelarrowexporter/factory.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowexporter // import "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter" - -import ( - "context" - "runtime" - "time" - - arrowpb "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1" - "github.com/open-telemetry/otel-arrow/collector/compression/zstd" - "github.com/open-telemetry/otel-arrow/collector/netstats" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configcompression" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/config/configopaque" - "go.opentelemetry.io/collector/config/configretry" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/exporter" - "go.opentelemetry.io/collector/exporter/exporterhelper" - "google.golang.org/grpc" - - "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow" - "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/metadata" -) - -// NewFactory creates a factory for OTLP exporter. -func NewFactory() exporter.Factory { - return exporter.NewFactory( - metadata.Type, - createDefaultConfig, - exporter.WithTraces(createTracesExporter, metadata.TracesStability), - exporter.WithMetrics(createMetricsExporter, metadata.MetricsStability), - exporter.WithLogs(createLogsExporter, metadata.LogsStability), - ) -} - -func createDefaultConfig() component.Config { - return &Config{ - TimeoutSettings: exporterhelper.NewDefaultTimeoutSettings(), - RetryConfig: configretry.NewDefaultBackOffConfig(), - QueueSettings: exporterhelper.NewDefaultQueueSettings(), - ClientConfig: configgrpc.ClientConfig{ - Headers: map[string]configopaque.String{}, - // Default to zstd compression - Compression: configcompression.TypeZstd, - // We almost read 0 bytes, so no need to tune ReadBufferSize. - WriteBufferSize: 512 * 1024, - // The `configgrpc` default is pick_first, - // which is not great for OTel Arrow exporters - // because it concentrates load at a single - // destination. - BalancerName: "round_robin", - }, - Arrow: ArrowConfig{ - NumStreams: runtime.NumCPU(), - MaxStreamLifetime: time.Hour, - - Zstd: zstd.DefaultEncoderConfig(), - Prioritizer: arrow.DefaultPrioritizer, - - // PayloadCompression is off by default because gRPC - // compression is on by default, above. - PayloadCompression: "", - }, - } -} - -func (exp *baseExporter) helperOptions() []exporterhelper.Option { - return []exporterhelper.Option{ - exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}), - exporterhelper.WithTimeout(exp.config.TimeoutSettings), - exporterhelper.WithRetry(exp.config.RetryConfig), - exporterhelper.WithQueue(exp.config.QueueSettings), - exporterhelper.WithStart(exp.start), - exporterhelper.WithShutdown(exp.shutdown), - } -} - -func gRPCName(desc grpc.ServiceDesc) string { - return netstats.GRPCStreamMethodName(desc, desc.Streams[0]) -} - -var ( - arrowTracesMethod = gRPCName(arrowpb.ArrowTracesService_ServiceDesc) - arrowMetricsMethod = gRPCName(arrowpb.ArrowMetricsService_ServiceDesc) - arrowLogsMethod = gRPCName(arrowpb.ArrowLogsService_ServiceDesc) -) - -func createArrowTracesStream(conn *grpc.ClientConn) arrow.StreamClientFunc { - return arrow.MakeAnyStreamClient(arrowTracesMethod, arrowpb.NewArrowTracesServiceClient(conn).ArrowTraces) -} - -func createTracesExporter( - ctx context.Context, - set exporter.Settings, - cfg component.Config, -) (exporter.Traces, error) { - exp, err := newExporter(cfg, set, createArrowTracesStream) - if err != nil { - return nil, err - } - return exporterhelper.NewTracesExporter(ctx, exp.settings, exp.config, - exp.pushTraces, - exp.helperOptions()..., - ) -} - -func createArrowMetricsStream(conn *grpc.ClientConn) arrow.StreamClientFunc { - return arrow.MakeAnyStreamClient(arrowMetricsMethod, arrowpb.NewArrowMetricsServiceClient(conn).ArrowMetrics) -} - -func createMetricsExporter( - ctx context.Context, - set exporter.Settings, - cfg component.Config, -) (exporter.Metrics, error) { - exp, err := newExporter(cfg, set, createArrowMetricsStream) - if err != nil { - return nil, err - } - return exporterhelper.NewMetricsExporter(ctx, exp.settings, exp.config, - exp.pushMetrics, - exp.helperOptions()..., - ) -} - -func createArrowLogsStream(conn *grpc.ClientConn) arrow.StreamClientFunc { - return arrow.MakeAnyStreamClient(arrowLogsMethod, arrowpb.NewArrowLogsServiceClient(conn).ArrowLogs) -} - -func createLogsExporter( - ctx context.Context, - set exporter.Settings, - cfg component.Config, -) (exporter.Logs, error) { - exp, err := newExporter(cfg, set, createArrowLogsStream) - if err != nil { - return nil, err - } - return exporterhelper.NewLogsExporter(ctx, exp.settings, exp.config, - exp.pushLogs, - exp.helperOptions()..., - ) -} diff --git a/collector/exporter/otelarrowexporter/factory_test.go b/collector/exporter/otelarrowexporter/factory_test.go deleted file mode 100644 index 6ba312fa..00000000 --- a/collector/exporter/otelarrowexporter/factory_test.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowexporter - -import ( - "context" - "path/filepath" - "runtime" - "testing" - "time" - - "github.com/open-telemetry/otel-arrow/collector/compression/zstd" - "github.com/open-telemetry/otel-arrow/collector/testutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/config/configcompression" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/config/configopaque" - "go.opentelemetry.io/collector/config/configretry" - "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/exporter/exporterhelper" - "go.opentelemetry.io/collector/exporter/exportertest" - - "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow" -) - -func TestCreateDefaultConfig(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.NotNil(t, cfg, "failed to create default config") - assert.NoError(t, componenttest.CheckConfigStruct(cfg)) - ocfg, ok := factory.CreateDefaultConfig().(*Config) - assert.True(t, ok) - assert.Equal(t, ocfg.RetryConfig, configretry.NewDefaultBackOffConfig()) - assert.Equal(t, ocfg.QueueSettings, exporterhelper.NewDefaultQueueSettings()) - assert.Equal(t, ocfg.TimeoutSettings, exporterhelper.NewDefaultTimeoutSettings()) - assert.Equal(t, ocfg.Compression, configcompression.TypeZstd) - assert.Equal(t, ocfg.Arrow, ArrowConfig{ - Disabled: false, - NumStreams: runtime.NumCPU(), - MaxStreamLifetime: time.Hour, - PayloadCompression: "", - Zstd: zstd.DefaultEncoderConfig(), - Prioritizer: arrow.DefaultPrioritizer, - }) -} - -func TestCreateMetricsExporter(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.ClientConfig.Endpoint = testutil.GetAvailableLocalAddress(t) - - set := exportertest.NewNopSettings() - oexp, err := factory.CreateMetricsExporter(context.Background(), set, cfg) - require.Nil(t, err) - require.NotNil(t, oexp) -} - -func TestCreateTracesExporter(t *testing.T) { - endpoint := testutil.GetAvailableLocalAddress(t) - tests := []struct { - name string - config Config - mustFailOnCreate bool - mustFailOnStart bool - }{ - { - name: "NoEndpoint", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: "", - }, - }, - mustFailOnCreate: true, - }, - { - name: "UseSecure", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - TLSSetting: configtls.ClientConfig{ - Insecure: false, - }, - }, - }, - }, - { - name: "Keepalive", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - Keepalive: &configgrpc.KeepaliveClientConfig{ - Time: 30 * time.Second, - Timeout: 25 * time.Second, - PermitWithoutStream: true, - }, - }, - }, - }, - { - name: "NoneCompression", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - Compression: "none", - }, - }, - }, - { - name: "GzipCompression", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - Compression: configcompression.TypeGzip, - }, - }, - }, - { - name: "SnappyCompression", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - Compression: configcompression.TypeSnappy, - }, - }, - }, - { - name: "ZstdCompression", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - Compression: configcompression.TypeZstd, - }, - }, - }, - { - name: "Headers", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - Headers: map[string]configopaque.String{ - "hdr1": "val1", - "hdr2": "val2", - }, - }, - }, - }, - { - name: "NumConsumers", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - }, - }, - }, - { - name: "CaCert", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - TLSSetting: configtls.ClientConfig{ - Config: configtls.Config{ - CAFile: filepath.Join("testdata", "test_cert.pem"), - }, - }, - }, - }, - }, - { - name: "CertPemFileError", - config: Config{ - ClientConfig: configgrpc.ClientConfig{ - Endpoint: endpoint, - TLSSetting: configtls.ClientConfig{ - Config: configtls.Config{ - CAFile: "nosuchfile", - }, - }, - }, - }, - mustFailOnStart: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - factory := NewFactory() - set := exportertest.NewNopSettings() - cfg := tt.config - consumer, err := factory.CreateTracesExporter(context.Background(), set, &cfg) - if tt.mustFailOnCreate { - assert.NotNil(t, err) - return - } - assert.NoError(t, err) - assert.NotNil(t, consumer) - err = consumer.Start(context.Background(), componenttest.NewNopHost()) - if tt.mustFailOnStart { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - // Shutdown is called even when Start fails - err = consumer.Shutdown(context.Background()) - if err != nil { - // Since the endpoint of OTLP exporter doesn't actually exist, - // exporter may already stop because it cannot connect. - assert.Equal(t, err.Error(), "rpc error: code = Canceled desc = grpc: the client connection is closing") - } - }) - } -} - -func TestCreateLogsExporter(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.ClientConfig.Endpoint = testutil.GetAvailableLocalAddress(t) - - set := exportertest.NewNopSettings() - oexp, err := factory.CreateLogsExporter(context.Background(), set, cfg) - require.Nil(t, err) - require.NotNil(t, oexp) -} - -func TestCreateArrowTracesExporter(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.ClientConfig.Endpoint = testutil.GetAvailableLocalAddress(t) - cfg.Arrow = ArrowConfig{ - NumStreams: 1, - } - set := exportertest.NewNopSettings() - oexp, err := factory.CreateTracesExporter(context.Background(), set, cfg) - require.Nil(t, err) - require.NotNil(t, oexp) -} diff --git a/collector/exporter/otelarrowexporter/generated_component_test.go b/collector/exporter/otelarrowexporter/generated_component_test.go deleted file mode 100644 index 0b323ce6..00000000 --- a/collector/exporter/otelarrowexporter/generated_component_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package otelarrowexporter - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component/componenttest" -) - -func TestComponentFactoryType(t *testing.T) { - require.Equal(t, "otelarrow", NewFactory().Type().String()) -} - -func TestComponentConfigStruct(t *testing.T) { - require.NoError(t, componenttest.CheckConfigStruct(NewFactory().CreateDefaultConfig())) -} diff --git a/collector/exporter/otelarrowexporter/generated_package_test.go b/collector/exporter/otelarrowexporter/generated_package_test.go deleted file mode 100644 index 8314a4b3..00000000 --- a/collector/exporter/otelarrowexporter/generated_package_test.go +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package otelarrowexporter - -import ( - "go.uber.org/goleak" - "testing" -) - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} diff --git a/collector/exporter/otelarrowexporter/go.mod b/collector/exporter/otelarrowexporter/go.mod deleted file mode 100644 index 74d383f0..00000000 --- a/collector/exporter/otelarrowexporter/go.mod +++ /dev/null @@ -1,98 +0,0 @@ -module github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter - -go 1.21.0 - -toolchain go1.22.2 - -require ( - github.com/apache/arrow/go/v16 v16.1.0 - github.com/open-telemetry/otel-arrow v0.24.0 - github.com/open-telemetry/otel-arrow/collector v0.24.0 - github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configcompression v1.9.1-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configopaque v1.9.1-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configretry v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e - go.opentelemetry.io/collector/extension v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/otel v1.27.0 - go.opentelemetry.io/otel/trace v1.27.0 - go.uber.org/goleak v1.3.0 - go.uber.org/mock v0.4.0 - go.uber.org/multierr v1.11.0 - go.uber.org/zap v1.27.0 - golang.org/x/net v0.25.0 - google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 - google.golang.org/grpc v1.64.0 - google.golang.org/protobuf v1.34.2 -) - -require ( - github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect - github.com/axiomhq/hyperloglog v0.0.0-20230201085229-3ddf4bad03dc // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/flatbuffers v24.3.25+incompatible // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.8 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/knadh/koanf/maps v0.1.1 // indirect - github.com/knadh/koanf/providers/confmap v0.1.0 // indirect - github.com/knadh/koanf/v2 v2.1.1 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mostynb/go-grpc-compression v1.2.3 // indirect - github.com/pierrec/lz4/v4 v4.1.21 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.54.0 // indirect - github.com/prometheus/procfs v0.15.0 // indirect - github.com/x448/float16 v0.8.4 // indirect - github.com/zeebo/xxh3 v1.0.2 // indirect - go.opentelemetry.io/collector/config/confignet v0.102.1 // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.102.1 // indirect - go.opentelemetry.io/collector/config/internal v0.102.1 // indirect - go.opentelemetry.io/collector/featuregate v1.9.0 // indirect - go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.49.0 // indirect - go.opentelemetry.io/otel/metric v1.27.0 // indirect - go.opentelemetry.io/otel/sdk v1.27.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) - -replace github.com/open-telemetry/otel-arrow/collector => ../.. - -replace github.com/open-telemetry/otel-arrow => ../../.. diff --git a/collector/exporter/otelarrowexporter/go.sum b/collector/exporter/otelarrowexporter/go.sum deleted file mode 100644 index 1af5477b..00000000 --- a/collector/exporter/otelarrowexporter/go.sum +++ /dev/null @@ -1,252 +0,0 @@ -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/apache/arrow/go/v16 v16.1.0 h1:dwgfOya6s03CzH9JrjCBx6bkVb4yPD4ma3haj9p7FXI= -github.com/apache/arrow/go/v16 v16.1.0/go.mod h1:9wnc9mn6vEDTRIm4+27pEjQpRKuTvBaessPoEXQzxWA= -github.com/axiomhq/hyperloglog v0.0.0-20230201085229-3ddf4bad03dc h1:Keo7wQ7UODUaHcEi7ltENhbAK2VgZjfat6mLy03tQzo= -github.com/axiomhq/hyperloglog v0.0.0-20230201085229-3ddf4bad03dc/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/brianvoe/gofakeit/v6 v6.17.0 h1:obbQTJeHfktJtiZzq0Q1bEpsNUs+yHrYlPVWt7BtmJ4= -github.com/brianvoe/gofakeit/v6 v6.17.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8= -github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= -github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= -github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= -github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= -github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= -github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= -github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= -github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mostynb/go-grpc-compression v1.2.3 h1:42/BKWMy0KEJGSdWvzqIyOZ95YcR9mLPqKctH7Uo//I= -github.com/mostynb/go-grpc-compression v1.2.3/go.mod h1:AghIxF3P57umzqM9yz795+y1Vjs47Km/Y2FE6ouQ7Lg= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= -github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= -github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= -github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c h1:UmlCWoLNgxxN906BHOXH06/TMeumOrDqdTXeRkYD6d4= -go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:gKjweCX6ve4F7X4RGV3kDN24Bg2eyV6MTCncnaPfRPA= -go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c h1:F17okJGeAtqIZZv/7mZvo6gunwPqdlt40znR0Vo1c1Q= -go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:AM5c/Ohhxj2j/vfCZrwKUD7PrcMpuCbo68rSBibV9U4= -go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c h1:dFcfo09PibmEsdBA2sMLbs+IyBWoPg7NwwEG6eWQRfY= -go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:EJ/PoRVuG1eowA3KQbnW8VonGHGz8jznCXpWG5U8+2A= -go.opentelemetry.io/collector/config/configcompression v1.9.1-0.20240611143128-7dfb57b9ad1c h1:BR/Gtt1BX7psCRIcrw6mIFUUTLfy884WMsQVgoItHeM= -go.opentelemetry.io/collector/config/configcompression v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:6+m0GKCv7JKzaumn7u80A2dLNCuYf5wdR87HWreoBO0= -go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c h1:ZleEsjYf+kxFV5aF8AWHZ4qPFIw/8EQyM2GTnm1ewHo= -go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:Qkn38t0e9y68UlXAWp+S3gsenh09LB9Ct5bJ56inDGQ= -go.opentelemetry.io/collector/config/confignet v0.102.1 h1:nSiAFQMzNCO4sDBztUxY73qFw4Vh0hVePq8+3wXUHtU= -go.opentelemetry.io/collector/config/confignet v0.102.1/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= -go.opentelemetry.io/collector/config/configopaque v1.9.1-0.20240611143128-7dfb57b9ad1c h1:+OJLmTVoFAzSSYgDW++ltj3ya5ZWjFOlL+sAp3Z4T9U= -go.opentelemetry.io/collector/config/configopaque v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:0xURn2sOy5j4fbaocpEYfM97HPGsiffkkVudSPyTJlM= -go.opentelemetry.io/collector/config/configretry v0.102.2-0.20240611143128-7dfb57b9ad1c h1:1s/JRy/6HKo22PgqsZSRrj3j0KXm0wZPLGZ/rfZlwvU= -go.opentelemetry.io/collector/config/configretry v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:P+RA0IA+QoxnDn4072uyeAk1RIoYiCbxYsjpKX5eFC4= -go.opentelemetry.io/collector/config/configtelemetry v0.102.1 h1:f/CYcrOkaHd+COIJ2lWnEgBCHfhEycpbow4ZhrGwAlA= -go.opentelemetry.io/collector/config/configtelemetry v0.102.1/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= -go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c h1:Foets1z7XMsh5KwEvGqFhEHem6Kx3xWjfUVUfLk6bF8= -go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:0/mMXy474cvCd4p4VSiZMTaHD/9LwdGbCqXvBPHkDSg= -go.opentelemetry.io/collector/config/internal v0.102.1 h1:HFsFD3xpHUuNHb8/UTz5crJw1cMHzsJQf/86sgD44hw= -go.opentelemetry.io/collector/config/internal v0.102.1/go.mod h1:Vig3dfeJJnuRe1kBNpszBzPoj5eYnR51wXbeq36Zfpg= -go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c h1:LOhGPowRmdpv7HU6HAFkdRvys41RWaijhGmWa7YBOsg= -go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:KgpS7UxH5rkd69CzAzlY2I1heH8Z7eNCZlHmwQBMxNg= -go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c h1:L/FPXl2OoOKniPw1hYzCOk6eljlcwCC681y4plDDE08= -go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:4EV8/Rh+KD6z75EjDDWthN50aFeeRqxsC589EpakV5E= -go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e h1:Z5SmlS/YR1O5FGQbXyv0oqNERCDDCMQVUWIIP+0NqM0= -go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:JUUGttX6dnz9+LDR+RzH2Imrr04SKwfpdtq42yknbnQ= -go.opentelemetry.io/collector/extension v0.102.2-0.20240611143128-7dfb57b9ad1c h1:kDjy3b4gMdXyYbkvJe2ARcfFsnfOsBLth6s7EB2Gp1s= -go.opentelemetry.io/collector/extension v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:UkgI/9uobPWsyKR17PdindQ4+CDL1hbVgpzUgfp9RRg= -go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c h1:+A3fAo4yg/eDswLz67kny4AlMfFWVY3L44IFbCyxZIk= -go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:m7aGTw7yl2Qe5kprqkJeky2wwVKeLYugu6eiS5XcXpY= -go.opentelemetry.io/collector/featuregate v1.9.0 h1:mC4/HnR5cx/kkG1RKOQAvHxxg5Ktmd9gpFdttPEXQtA= -go.opentelemetry.io/collector/featuregate v1.9.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= -go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c h1:f8L2r0f684bJAAZDoTvEWccx34C3kQsePNwy8KzTPqM= -go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE= -go.opentelemetry.io/collector/pdata/testdata v0.102.1 h1:S3idZaJxy8M7mCC4PG4EegmtiSaOuh6wXWatKIui8xU= -go.opentelemetry.io/collector/pdata/testdata v0.102.1/go.mod h1:JEoSJTMgeTKyGxoMRy48RMYyhkA5vCCq/abJq9B6vXs= -go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e h1:Y1zH80bbNuJWd63T5PjpFs4+NrAl8LvMBxqPCf6XBrM= -go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:A/oJHfYc+1auPv6gbp0B/kR8DLtFIDCB/Z/3nDlHVQs= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= -go.opentelemetry.io/otel/exporters/prometheus v0.49.0 h1:Er5I1g/YhfYv9Affk9nJLfH/+qCCVVg1f2R9AbJfqDQ= -go.opentelemetry.io/otel/exporters/prometheus v0.49.0/go.mod h1:KfQ1wpjf3zsHjzP149P4LyAwWRupc6c7t1ZJ9eXpKQM= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= -go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= -go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= -gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 h1:Q2RxlXqh1cgzzUgV261vBO2jI5R/3DD1J2pM0nI4NhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/collector/exporter/otelarrowexporter/internal/arrow/Makefile b/collector/exporter/otelarrowexporter/internal/arrow/Makefile deleted file mode 100644 index f70e8145..00000000 --- a/collector/exporter/otelarrowexporter/internal/arrow/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# Generate the mock files -.PHONY: mockgen -mockgen: - go install go.uber.org/mock/mockgen@latest - mkdir -p ./grpcmock - mockgen -package grpcmock google.golang.org/grpc/credentials PerRPCCredentials > grpcmock/credentials.go diff --git a/collector/exporter/otelarrowexporter/internal/arrow/README.md b/collector/exporter/otelarrowexporter/internal/arrow/README.md deleted file mode 100644 index 33aa03f6..00000000 --- a/collector/exporter/otelarrowexporter/internal/arrow/README.md +++ /dev/null @@ -1,127 +0,0 @@ -# OTel-Arrow streaming exporter - -## Design - -The principle components of the OTel-Arrow Exporter are: - -1. Sender logic: Submits data to a stream, waits for response or timeout. -2. Prioritizer logic: Intermediary, configurable policy for choice of stream -3. Manager logic: Oversees multiple stream lifetimes, decides to downgrade -4. Stream logic: A single gRPC OTAP stream, consisting of independent - reader and writer subroutines. - -A request goes through the following steps following arrival inside -this component. - -![Block diagram of OpenTelemetry Arrow Exporter](./design.png) - -The sender computes per-request metadata including auth headers and -the original uncompressed data size, while still in the caller's -context. Then, it checks with the prioritizer for the downgrade -condition, otherwise submits the item to a stream via the prioritizer. - -The prioritizer dictates which current stream receives arriving items -of data to balance load across streams. The prioritizer -implementations are described in a section below. - -The stream manager is responsible for supervising individual streams -and outcomes. The stream manager is responsible for the decision to -downgrade to standard OTLP when it appears that Arrow is unsupported. - -The individual stream is made up of two subroutines, _reader_ and -_writer_, executing independently and a "waiters" map. - -The stream writer receives work from the sender logic (via the -prioritizer), encodes the data using the current OTel-Arrow stream -state, and writes it via gRPC. As soon as the data is entered into -the gRPC write buffer, the stream writer continues to the next -request. This repeats until the stream maximum lifetime is reached. - -The stream reader receives status data from the corresponding -OTel-Arrow receiver, translates the responses into `error` objects, -and returns them to the awaiting sender logic. - -### Context hierarchy - -There are three levels of Context object used inside the Exporter, in -addition to the Context object that arrives from the pipeline when -data is sent. - -The top-level Context corresponds with the component lifetime. This -context is canceled when the component is shutdown. - -As a child of the top-level Context, the downgrade Context is one that -will be canceled when the stream manager decides to downgrade. - -As a child of the downgrade Context, the individual Stream context is -one that will be canceled when the stream itself ends or closes in any -way. - -In request context, the Exporter sender logic arrives with a timeout -that is configured in the `exporterhelper`, via a `timeout` field in -the configuration. - -### Stream lifecycle - -When a stream starts, it first calculates a per-stream "jitter" factor -which is subtracted from the configured maximum stream lifetime. The -stream will send new requests until the timer expires, and then it -will `CloseSend()` the gRPC stream, which signals to the receiver to -initiate stream shutdown. When the receiver has sent its last -response, it returns and the exporter sees end-of-file. - -When either the reader or the writer see an error condition, they will -cancel the stream and cause the other subroutine to return. Both -errors are logged, and the stream will be restarted. - -The stream begins with a `*streamWorkState`, a reference to a -structure including the stream `input` (`chan<- writeItem`) and -`waiters` (`map[BatchID]<-chan error`). The `*streamWorkState` is -re-used across streams, passed by the stream manager from the exiting -stream to the new stream to use when it starts. - -In situations where a stream breaks while some work is in flight, the -special `ErrStreamRestarting` error code is returned to indicate that -a stream broke, a condition not to the data. This causes the sender -logic to immediately restart the operation on a new stream, instead of -returning a retryable error code to the `exporterhelper` logic, which -would delay before retrying. - -### Downgrade - -The downgrade mechanism is implemented by canceling the special -Context that was created for this purpose. Once downgrade has -happened, the prioritizer is expected to return a `nil` Stream and -`nil` error value. When the sender logic sees this condition, it -returns to the standard OTLP Exporter that called in to this package. - -Synchronization around the downgrade is relatively simple, however it -is required to leave behind one or more goroutines in the background, -in case of races between the prioritizer and sender logic. There is a -method named `drain()` that will reply with `ErrStreamRestarting` to -any `writeItem` values that arrive after downgrade happens. - -TODO: Fix https://github.com/open-telemetry/otel-arrow/issues/87. -Note that re-use of `*streamWorkState` across restart may lead to -abandoned work when some streams are unavailable, because no streams -restart following unavaiable, instead the manager waits for downgrade. - -### Prioritizers - -#### LeastLoadedN - -This prioritizer randomly selects N active streams and chooses the one -with the least outstanding number of items of data. This is -accomplished using a number of intermediary subroutines, which -repeatly get a next item of data, pick a stream, and place the item -into the stream's input channel. To select a least-loaded prioritizer, -use "leastloaded" followed by N (e.g., "leastloaded2", "leastloaded10"). - -TODO: Note that this prioritizer does not consider immediate readiness -of the corresponding stream, making it possible for intermediate -prioritizer subroutines to block when there is more than one pending -work item for a given stream. With numStreams intermediate -subroutines available, there is a chance of blocking when in fact a -more-loaded stream is ready. Consider a change to select from only -the immediately ready streams, although recognize that it will take -a special case when no streams are immediately ready. diff --git a/collector/exporter/otelarrowexporter/internal/arrow/bestofn.go b/collector/exporter/otelarrowexporter/internal/arrow/bestofn.go deleted file mode 100644 index 1c4f2f34..00000000 --- a/collector/exporter/otelarrowexporter/internal/arrow/bestofn.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package arrow // import "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow" - -import ( - "context" - "math/rand" - "runtime" - "sort" - "time" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// bestOfNPrioritizer is a prioritizer that selects a less-loaded stream to write. -// https://smallrye.io/smallrye-stork/1.1.1/load-balancer/power-of-two-choices/ -type bestOfNPrioritizer struct { - doneCancel - - // input from the pipeline, as processed data with headers and - // a return channel for the result. This channel is never - // closed and is buffered. At shutdown, items of telemetry can - // be left in this channel, but users are expected to complete - // their requests before calling shutdown (and the collector's - // graph package ensures this). - input chan writeItem - - // state tracks the work being handled by all streams. - state []*streamWorkState - - // numChoices is the number of streams to consder in each decision. - numChoices int - - // loadFunc is the load function. - loadFunc loadFunc -} - -type loadFunc func(*streamWorkState) float64 - -type streamSorter struct { - work *streamWorkState - load float64 -} - -var _ streamPrioritizer = &bestOfNPrioritizer{} - -func newBestOfNPrioritizer(dc doneCancel, numChoices, numStreams int, lf loadFunc, maxLifetime time.Duration) (*bestOfNPrioritizer, []*streamWorkState) { - var state []*streamWorkState - - // Limit numChoices to the number of streams. - numChoices = min(numStreams, numChoices) - - for i := 0; i < numStreams; i++ { - ws := &streamWorkState{ - maxStreamLifetime: addJitter(maxLifetime), - waiters: map[int64]chan<- error{}, - toWrite: make(chan writeItem, 1), - } - - state = append(state, ws) - } - - lp := &bestOfNPrioritizer{ - doneCancel: dc, - input: make(chan writeItem, runtime.NumCPU()), - state: state, - numChoices: numChoices, - loadFunc: lf, - } - - for i := 0; i < numStreams; i++ { - // TODO It's not clear if/when the the prioritizer can - // become a bottleneck. - go lp.run() - } - - return lp, state -} - -func (lp *bestOfNPrioritizer) downgrade(ctx context.Context) { - for _, ws := range lp.state { - go drain(ws.toWrite, ctx.Done()) - } -} - -func (lp *bestOfNPrioritizer) sendOne(item writeItem, rnd *rand.Rand, tmp []streamSorter) { - stream := lp.streamFor(item, rnd, tmp) - writeCh := stream.toWrite - select { - case writeCh <- item: - return - - case <-lp.done: - // All other cases: signal restart. - } - item.errCh <- ErrStreamRestarting -} - -func (lp *bestOfNPrioritizer) run() { - tmp := make([]streamSorter, len(lp.state)) - rnd := rand.New(rand.NewSource(rand.Int63())) - for { - select { - case <-lp.done: - return - case item := <-lp.input: - lp.sendOne(item, rnd, tmp) - } - } -} - -// sendAndWait implements streamWriter -func (lp *bestOfNPrioritizer) sendAndWait(ctx context.Context, errCh <-chan error, wri writeItem) error { - select { - case <-lp.done: - return ErrStreamRestarting - case <-ctx.Done(): - return status.Errorf(codes.Canceled, "stream wait: %v", ctx.Err()) - case lp.input <- wri: - return waitForWrite(ctx, errCh, lp.done) - } -} - -func (lp *bestOfNPrioritizer) nextWriter() streamWriter { - select { - case <-lp.done: - // In case of downgrade, return nil to return into a - // non-Arrow code path. - return nil - default: - // Fall through to sendAndWait(). - return lp - } -} - -func (lp *bestOfNPrioritizer) streamFor(_ writeItem, rnd *rand.Rand, tmp []streamSorter) *streamWorkState { - // Place all streams into the temporary slice. - for idx, item := range lp.state { - tmp[idx].work = item - } - // Select numChoices at random by shifting the selection into the start - // of the temporary slice. - for i := 0; i < lp.numChoices; i++ { - pick := rnd.Intn(lp.numChoices - i) - tmp[i], tmp[i+pick] = tmp[i+pick], tmp[i] - } - for i := 0; i < lp.numChoices; i++ { - // TODO: skip channels w/ a pending item (maybe) - tmp[i].load = lp.loadFunc(tmp[i].work) - } - sort.Slice(tmp[0:lp.numChoices], func(i, j int) bool { - return tmp[i].load < tmp[j].load - }) - return tmp[0].work -} diff --git a/collector/exporter/otelarrowexporter/internal/arrow/common_test.go b/collector/exporter/otelarrowexporter/internal/arrow/common_test.go deleted file mode 100644 index ee97e2de..00000000 --- a/collector/exporter/otelarrowexporter/internal/arrow/common_test.go +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package arrow - -import ( - "context" - "fmt" - "io" - - arrowpb "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1" - arrowCollectorMock "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1/mock" - "github.com/open-telemetry/otel-arrow/collector/testdata" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenttest" - "go.uber.org/mock/gomock" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "go.uber.org/zap/zaptest" - "go.uber.org/zap/zaptest/observer" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/status" - - "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow/grpcmock" -) - -var ( - twoTraces = testdata.GenerateTraces(2) - twoMetrics = testdata.GenerateMetrics(2) - twoLogs = testdata.GenerateLogs(2) -) - -type testChannel interface { - onRecv(context.Context) func() (*arrowpb.BatchStatus, error) - onSend(context.Context) func(*arrowpb.BatchArrowRecords) error - onConnect(context.Context) error - onCloseSend() func() error -} - -type commonTestCase struct { - ctrl *gomock.Controller - telset component.TelemetrySettings - observedLogs *observer.ObservedLogs - traceClient StreamClientFunc - traceCall *gomock.Call - perRPCCredentials credentials.PerRPCCredentials - requestMetadataCall *gomock.Call -} - -type noisyTest bool - -const Noisy noisyTest = true -const NotNoisy noisyTest = false - -func newTestTelemetry(t zaptest.TestingT, noisy noisyTest) (component.TelemetrySettings, *observer.ObservedLogs) { - telset := componenttest.NewNopTelemetrySettings() - if noisy { - return telset, nil - } - core, obslogs := observer.New(zapcore.InfoLevel) - telset.Logger = zap.New(zapcore.NewTee(core, zaptest.NewLogger(t).Core())) - return telset, obslogs -} - -type z2m struct { - zaptest.TestingT -} - -var _ gomock.TestReporter = z2m{} - -func (t z2m) Fatalf(format string, args ...any) { - t.Errorf(format, args...) - t.Fail() -} - -func newCommonTestCase(t zaptest.TestingT, noisy noisyTest) *commonTestCase { - ctrl := gomock.NewController(z2m{t}) - telset, obslogs := newTestTelemetry(t, noisy) - - creds := grpcmock.NewMockPerRPCCredentials(ctrl) - creds.EXPECT().RequireTransportSecurity().Times(0) // unused interface method - requestMetadataCall := creds.EXPECT().GetRequestMetadata( - gomock.Any(), // context.Context - gomock.Any(), // ...string (unused `uri` parameter) - ).Times(0) - - traceClient := arrowCollectorMock.NewMockArrowTracesServiceClient(ctrl) - - traceCall := traceClient.EXPECT().ArrowTraces( - gomock.Any(), // context.Context - gomock.Any(), // ...grpc.CallOption - ).Times(0) - return &commonTestCase{ - ctrl: ctrl, - telset: telset, - observedLogs: obslogs, - traceClient: MakeAnyStreamClient("ArrowTraces", traceClient.ArrowTraces), - traceCall: traceCall, - perRPCCredentials: creds, - requestMetadataCall: requestMetadataCall, - } -} - -type commonTestStream struct { - anyStreamClient AnyStreamClient - ctxCall *gomock.Call - sendCall *gomock.Call - recvCall *gomock.Call - closeSendCall *gomock.Call -} - -func (ctc *commonTestCase) newMockStream(ctx context.Context) *commonTestStream { - client := arrowCollectorMock.NewMockArrowTracesService_ArrowTracesClient(ctc.ctrl) - - testStream := &commonTestStream{ - anyStreamClient: client, - ctxCall: client.EXPECT().Context().AnyTimes().Return(ctx), - sendCall: client.EXPECT().Send( - gomock.Any(), // *arrowpb.BatchArrowRecords - ).Times(0), - recvCall: client.EXPECT().Recv().Times(0), - closeSendCall: client.EXPECT().CloseSend().Times(0), - } - return testStream -} - -// returnNewStream applies the list of test channels in order to -// construct new streams. The final entry is re-used for new streams -// when it is reached. -func (ctc *commonTestCase) returnNewStream(hs ...testChannel) func(context.Context, ...grpc.CallOption) ( - arrowpb.ArrowTracesService_ArrowTracesClient, - error, -) { - var pos int - return func(ctx context.Context, _ ...grpc.CallOption) ( - arrowpb.ArrowTracesService_ArrowTracesClient, - error, - ) { - h := hs[pos] - if pos < len(hs) { - pos++ - } - if err := h.onConnect(ctx); err != nil { - return nil, err - } - str := ctc.newMockStream(ctx) - str.sendCall.AnyTimes().DoAndReturn(h.onSend(ctx)) - str.recvCall.AnyTimes().DoAndReturn(h.onRecv(ctx)) - str.closeSendCall.AnyTimes().DoAndReturn(h.onCloseSend()) - return str.anyStreamClient, nil - } -} - -// repeatedNewStream returns a stream configured with a new test -// channel on every ArrowStream() request. -func (ctc *commonTestCase) repeatedNewStream(nc func() testChannel) func(context.Context, ...grpc.CallOption) ( - arrowpb.ArrowTracesService_ArrowTracesClient, - error, -) { - return func(ctx context.Context, _ ...grpc.CallOption) ( - arrowpb.ArrowTracesService_ArrowTracesClient, - error, - ) { - h := nc() - if err := h.onConnect(ctx); err != nil { - return nil, err - } - str := ctc.newMockStream(ctx) - str.sendCall.AnyTimes().DoAndReturn(h.onSend(ctx)) - str.recvCall.AnyTimes().DoAndReturn(h.onRecv(ctx)) - str.closeSendCall.AnyTimes().DoAndReturn(h.onCloseSend()) - return str.anyStreamClient, nil - } -} - -// healthyTestChannel accepts the connection and returns an OK status immediately. -type healthyTestChannel struct { - sent chan *arrowpb.BatchArrowRecords - recv chan *arrowpb.BatchStatus -} - -func newHealthyTestChannel() *healthyTestChannel { - return &healthyTestChannel{ - sent: make(chan *arrowpb.BatchArrowRecords), - recv: make(chan *arrowpb.BatchStatus), - } -} - -func (tc *healthyTestChannel) sendChannel() chan *arrowpb.BatchArrowRecords { - return tc.sent -} - -func (tc *healthyTestChannel) onConnect(_ context.Context) error { - return nil -} - -func (tc *healthyTestChannel) onCloseSend() func() error { - return func() error { - close(tc.sent) - return nil - } -} - -func (tc *healthyTestChannel) onSend(ctx context.Context) func(*arrowpb.BatchArrowRecords) error { - return func(req *arrowpb.BatchArrowRecords) error { - select { - case tc.sendChannel() <- req: - return nil - case <-ctx.Done(): - return ctx.Err() - } - } -} - -func (tc *healthyTestChannel) onRecv(ctx context.Context) func() (*arrowpb.BatchStatus, error) { - return func() (*arrowpb.BatchStatus, error) { - select { - case recv, ok := <-tc.recv: - if !ok { - return nil, io.EOF - } - - return recv, nil - case <-ctx.Done(): - return &arrowpb.BatchStatus{}, ctx.Err() - } - } -} - -// unresponsiveTestChannel accepts the connection and receives data, -// but never responds with status OK. -type unresponsiveTestChannel struct { - ch chan struct{} -} - -func newUnresponsiveTestChannel() *unresponsiveTestChannel { - return &unresponsiveTestChannel{ - ch: make(chan struct{}), - } -} - -func (tc *unresponsiveTestChannel) onConnect(_ context.Context) error { - return nil -} - -func (tc *unresponsiveTestChannel) onCloseSend() func() error { - return func() error { - return nil - } -} - -func (tc *unresponsiveTestChannel) onSend(ctx context.Context) func(*arrowpb.BatchArrowRecords) error { - return func(_ *arrowpb.BatchArrowRecords) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - return nil - } - } -} - -func (tc *unresponsiveTestChannel) onRecv(ctx context.Context) func() (*arrowpb.BatchStatus, error) { - return func() (*arrowpb.BatchStatus, error) { - select { - case <-tc.ch: - return nil, io.EOF - case <-ctx.Done(): - return &arrowpb.BatchStatus{}, ctx.Err() - } - } -} - -func (tc *unresponsiveTestChannel) unblock() { - close(tc.ch) -} - -// unsupportedTestChannel mimics gRPC's behavior when there is no -// arrow stream service registered with the server. -type arrowUnsupportedTestChannel struct { -} - -func newArrowUnsupportedTestChannel() *arrowUnsupportedTestChannel { - return &arrowUnsupportedTestChannel{} -} - -func (tc *arrowUnsupportedTestChannel) onConnect(_ context.Context) error { - // Note: this matches gRPC's apparent behavior. the stream - // connection succeeds and the unsupported code is returned to - // the Recv() call. - return nil -} - -func (tc *arrowUnsupportedTestChannel) onCloseSend() func() error { - return func() error { - return nil - } -} - -func (tc *arrowUnsupportedTestChannel) onSend(ctx context.Context) func(*arrowpb.BatchArrowRecords) error { - return func(_ *arrowpb.BatchArrowRecords) error { - <-ctx.Done() - return ctx.Err() - } -} - -func (tc *arrowUnsupportedTestChannel) onRecv(_ context.Context) func() (*arrowpb.BatchStatus, error) { - return func() (*arrowpb.BatchStatus, error) { - err := status.Error(codes.Unimplemented, "arrow will not be served") - return &arrowpb.BatchStatus{}, err - } -} - -// disconnectedTestChannel allows the connection to time out. -type disconnectedTestChannel struct { -} - -func newDisconnectedTestChannel() *disconnectedTestChannel { - return &disconnectedTestChannel{} -} - -func (tc *disconnectedTestChannel) onConnect(ctx context.Context) error { - <-ctx.Done() - return ctx.Err() -} - -func (tc *disconnectedTestChannel) onCloseSend() func() error { - return func() error { - panic("unreachable") - } -} - -func (tc *disconnectedTestChannel) onSend(_ context.Context) func(*arrowpb.BatchArrowRecords) error { - return func(_ *arrowpb.BatchArrowRecords) error { - panic("unreachable") - } -} - -func (tc *disconnectedTestChannel) onRecv(_ context.Context) func() (*arrowpb.BatchStatus, error) { - return func() (*arrowpb.BatchStatus, error) { - panic("unreachable") - } -} - -// sendErrorTestChannel returns an error in Send() -type sendErrorTestChannel struct { - release chan struct{} -} - -func newSendErrorTestChannel() *sendErrorTestChannel { - return &sendErrorTestChannel{ - release: make(chan struct{}), - } -} - -func (tc *sendErrorTestChannel) onConnect(_ context.Context) error { - return nil -} - -func (tc *sendErrorTestChannel) onCloseSend() func() error { - return func() error { - return nil - } -} - -func (tc *sendErrorTestChannel) onSend(_ context.Context) func(*arrowpb.BatchArrowRecords) error { - return func(*arrowpb.BatchArrowRecords) error { - return io.EOF - } -} - -func (tc *sendErrorTestChannel) unblock() { - close(tc.release) -} - -func (tc *sendErrorTestChannel) onRecv(_ context.Context) func() (*arrowpb.BatchStatus, error) { - return func() (*arrowpb.BatchStatus, error) { - <-tc.release - return &arrowpb.BatchStatus{}, io.EOF - } -} - -// connectErrorTestChannel returns an error from the ArrowTraces() call -type connectErrorTestChannel struct { -} - -func newConnectErrorTestChannel() *connectErrorTestChannel { - return &connectErrorTestChannel{} -} - -func (tc *connectErrorTestChannel) onConnect(_ context.Context) error { - return fmt.Errorf("test connect error") -} - -func (tc *connectErrorTestChannel) onCloseSend() func() error { - return func() error { - panic("unreachable") - } -} - -func (tc *connectErrorTestChannel) onSend(_ context.Context) func(*arrowpb.BatchArrowRecords) error { - return func(*arrowpb.BatchArrowRecords) error { - panic("not reached") - } -} - -func (tc *connectErrorTestChannel) onRecv(_ context.Context) func() (*arrowpb.BatchStatus, error) { - return func() (*arrowpb.BatchStatus, error) { - panic("not reached") - } -} diff --git a/collector/exporter/otelarrowexporter/internal/arrow/design.png b/collector/exporter/otelarrowexporter/internal/arrow/design.png deleted file mode 100644 index 642a11fb299c2b900a5688d52356eac3d9a6345d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149806 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 z 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 z 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
J8 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 1000 - }, 10*time.Second, 5*time.Millisecond) - - cancel() - wg.Wait() - require.NoError(t, tc.exporter.Shutdown(bg)) -} - -// TestArrowExporterStreaming tests 10 sends in a row. -func TestArrowExporterStreaming(t *testing.T) { - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - tc := newSingleStreamTestCase(t, pname) - channel := newHealthyTestChannel() - - tc.traceCall.AnyTimes().DoAndReturn(tc.returnNewStream(channel)) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - require.NoError(t, tc.exporter.Start(ctx)) - - var expectOutput []ptrace.Traces - var actualOutput []ptrace.Traces - testCon := arrowRecord.NewConsumer() - - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - for data := range channel.sendChannel() { - traces, err := testCon.TracesFrom(data) - require.NoError(t, err) - require.Equal(t, 1, len(traces)) - actualOutput = append(actualOutput, traces[0]) - channel.recv <- statusOKFor(data.BatchId) - } - }() - - for times := 0; times < 10; times++ { - input := testdata.GenerateTraces(2) - - sent, err := tc.exporter.SendAndWait(context.Background(), input) - require.NoError(t, err) - require.True(t, sent) - - expectOutput = append(expectOutput, input) - } - // Stop the test conduit started above. - cancel() - wg.Wait() - - // As this equality check doesn't support out of order slices, - // we sort the slices directly in the GenerateTraces function. - require.Equal(t, expectOutput, actualOutput) - require.NoError(t, tc.exporter.Shutdown(ctx)) - }) - } -} - -// TestArrowExporterHeaders tests a mix of outgoing context headers. -func TestArrowExporterHeaders(t *testing.T) { - tc := newSingleStreamMetadataTestCase(t) - channel := newHealthyTestChannel() - - tc.traceCall.AnyTimes().DoAndReturn(tc.returnNewStream(channel)) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - require.NoError(t, tc.exporter.Start(ctx)) - - var expectOutput []metadata.MD - var actualOutput []metadata.MD - - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - md := metadata.MD{} - hpd := hpack.NewDecoder(4096, func(f hpack.HeaderField) { - md[f.Name] = append(md[f.Name], f.Value) - }) - for data := range channel.sendChannel() { - if len(data.Headers) == 0 { - actualOutput = append(actualOutput, nil) - } else { - _, err := hpd.Write(data.Headers) - require.NoError(t, err) - actualOutput = append(actualOutput, md) - md = metadata.MD{} - } - channel.recv <- statusOKFor(data.BatchId) - } - }() - - for times := 0; times < 10; times++ { - input := testdata.GenerateTraces(2) - - if times%2 == 1 { - md := metadata.MD{ - "expected1": []string{"metadata1"}, - "expected2": []string{fmt.Sprint(times)}, - "otlp-pdata-size": []string{"329"}, - } - expectOutput = append(expectOutput, md) - } else { - expectOutput = append(expectOutput, metadata.MD{ - "otlp-pdata-size": []string{"329"}, - }) - } - - sent, err := tc.exporter.SendAndWait(context.Background(), input) - require.NoError(t, err) - require.True(t, sent) - } - // Stop the test conduit started above. - cancel() - wg.Wait() - - require.Equal(t, expectOutput, actualOutput) - require.NoError(t, tc.exporter.Shutdown(ctx)) -} - -// TestArrowExporterIsTraced tests whether trace and span ID are -// propagated. -func TestArrowExporterIsTraced(t *testing.T) { - otel.SetTextMapPropagator(propagation.TraceContext{}) - - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - tc := newSingleStreamTestCase(t, pname) - channel := newHealthyTestChannel() - - tc.traceCall.AnyTimes().DoAndReturn(tc.returnNewStream(channel)) - - ctx, cancel := context.WithCancel(context.Background()) - require.NoError(t, tc.exporter.Start(ctx)) - - var expectOutput []metadata.MD - var actualOutput []metadata.MD - - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - md := metadata.MD{} - hpd := hpack.NewDecoder(4096, func(f hpack.HeaderField) { - md[f.Name] = append(md[f.Name], f.Value) - }) - for data := range channel.sendChannel() { - if len(data.Headers) == 0 { - actualOutput = append(actualOutput, nil) - } else { - _, err := hpd.Write(data.Headers) - require.NoError(t, err) - actualOutput = append(actualOutput, md) - md = metadata.MD{} - } - channel.recv <- statusOKFor(data.BatchId) - } - }() - - for times := 0; times < 10; times++ { - input := testdata.GenerateTraces(2) - callCtx := context.Background() - - if times%2 == 1 { - callCtx = trace.ContextWithSpanContext(callCtx, - trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: [16]byte{byte(times), 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, - SpanID: [8]byte{byte(times), 1, 2, 3, 4, 5, 6, 7}, - }), - ) - expectMap := map[string]string{} - propagation.TraceContext{}.Inject(callCtx, propagation.MapCarrier(expectMap)) - - md := metadata.MD{ - "traceparent": []string{expectMap["traceparent"]}, - "otlp-pdata-size": []string{"329"}, - } - expectOutput = append(expectOutput, md) - } else { - expectOutput = append(expectOutput, metadata.MD{ - "otlp-pdata-size": []string{"329"}, - }) - } - - sent, err := tc.exporter.SendAndWait(callCtx, input) - require.NoError(t, err) - require.True(t, sent) - } - // Stop the test conduit started above. - cancel() - wg.Wait() - - require.Equal(t, expectOutput, actualOutput) - require.NoError(t, tc.exporter.Shutdown(ctx)) - }) - } -} - -func TestAddJitter(t *testing.T) { - require.Equal(t, time.Duration(0), addJitter(0)) - - // Expect no more than 5% less in each trial. - for i := 0; i < 100; i++ { - x := addJitter(20 * time.Minute) - require.LessOrEqual(t, 19*time.Minute, x) - require.Less(t, x, 20*time.Minute) - } -} - -// TestArrowExporterStreamLifetimeAndShutdown exercises multiple -// stream lifetimes and then shuts down, inspects the logs for -// legibility. -func TestArrowExporterStreamLifetimeAndShutdown(t *testing.T) { - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - for _, numStreams := range []int{1, 2, 8} { - t.Run(fmt.Sprint(numStreams), func(t *testing.T) { - tc := newShortLifetimeStreamTestCase(t, pname, numStreams) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - var wg sync.WaitGroup - - var expectCount uint64 - var actualCount uint64 - - tc.traceCall.AnyTimes().DoAndReturn(func(ctx context.Context, opts ...grpc.CallOption) ( - arrowpb.ArrowTracesService_ArrowTracesClient, - error, - ) { - wg.Add(1) - channel := newHealthyTestChannel() - - go func() { - defer wg.Done() - testCon := arrowRecord.NewConsumer() - - for data := range channel.sendChannel() { - traces, err := testCon.TracesFrom(data) - require.NoError(t, err) - require.Equal(t, 1, len(traces)) - atomic.AddUint64(&actualCount, 1) - channel.recv <- statusOKFor(data.BatchId) - } - - // Closing the recv channel causes the exporter to see EOF. - close(channel.recv) - }() - - return tc.returnNewStream(channel)(ctx, opts...) - }) - - require.NoError(t, tc.exporter.Start(ctx)) - - start := time.Now() - // This is 10 stream lifetimes using the "ShortLifetime" test. - for time.Since(start) < 5*time.Second { - input := testdata.GenerateTraces(2) - - sent, err := tc.exporter.SendAndWait(ctx, input) - require.NoError(t, err) - require.True(t, sent) - - expectCount++ - } - - require.NoError(t, tc.exporter.Shutdown(ctx)) - - require.Equal(t, expectCount, actualCount) - - cancel() - wg.Wait() - - require.Empty(t, tc.observedLogs.All()) - }) - } - }) - } -} - -func BenchmarkLeastLoadedTwo4(b *testing.B) { - benchmarkPrioritizer(b, 4, LeastLoadedTwoPrioritizer) -} - -func BenchmarkLeastLoadedTwo8(b *testing.B) { - benchmarkPrioritizer(b, 8, LeastLoadedTwoPrioritizer) -} - -func BenchmarkLeastLoadedTwo16(b *testing.B) { - benchmarkPrioritizer(b, 16, LeastLoadedTwoPrioritizer) -} - -func BenchmarkLeastLoadedTwo32(b *testing.B) { - benchmarkPrioritizer(b, 32, LeastLoadedTwoPrioritizer) -} - -func BenchmarkLeastLoadedTwo64(b *testing.B) { - benchmarkPrioritizer(b, 64, LeastLoadedTwoPrioritizer) -} - -func BenchmarkLeastLoadedTwo128(b *testing.B) { - benchmarkPrioritizer(b, 128, LeastLoadedTwoPrioritizer) -} - -func BenchmarkLeastLoadedFour4(b *testing.B) { - benchmarkPrioritizer(b, 4, LeastLoadedFourPrioritizer) -} - -func BenchmarkLeastLoadedFour8(b *testing.B) { - benchmarkPrioritizer(b, 8, LeastLoadedFourPrioritizer) -} - -func BenchmarkLeastLoadedFour16(b *testing.B) { - benchmarkPrioritizer(b, 16, LeastLoadedFourPrioritizer) -} - -func BenchmarkLeastLoadedFour32(b *testing.B) { - benchmarkPrioritizer(b, 32, LeastLoadedFourPrioritizer) -} - -func BenchmarkLeastLoadedFour64(b *testing.B) { - benchmarkPrioritizer(b, 64, LeastLoadedFourPrioritizer) -} - -func BenchmarkLeastLoadedFour128(b *testing.B) { - benchmarkPrioritizer(b, 128, LeastLoadedFourPrioritizer) -} - -func benchmarkPrioritizer(b *testing.B, numStreams int, pname PrioritizerName) { - tc := newExporterTestCaseCommon(z2m{b}, pname, Noisy, defaultMaxStreamLifetime, numStreams, true, nil) - - var wg sync.WaitGroup - var cnt atomic.Int32 - - tc.traceCall.AnyTimes().DoAndReturn(func(ctx context.Context, opts ...grpc.CallOption) ( - arrowpb.ArrowTracesService_ArrowTracesClient, - error, - ) { - wg.Add(1) - num := cnt.Add(1) - channel := newHealthyTestChannel() - - delay := time.Duration(num) * time.Millisecond - - go func() { - defer wg.Done() - var mine sync.WaitGroup - for data := range channel.sendChannel() { - mine.Add(1) - go func(<-chan time.Time) { - defer mine.Done() - channel.recv <- statusOKFor(data.BatchId) - }(time.After(delay)) - } - - mine.Wait() - - close(channel.recv) - }() - - return tc.returnNewStream(channel)(ctx, opts...) - }) - - bg, cancel := context.WithCancel(context.Background()) - defer cancel() - if err := tc.exporter.Start(bg); err != nil { - b.Errorf("start failed: %v", err) - return - } - - input := testdata.GenerateTraces(2) - - wg.Add(1) - defer func() { - if err := tc.exporter.Shutdown(bg); err != nil { - b.Errorf("shutdown failed: %v", err) - } - wg.Done() - wg.Wait() - }() - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - sent, err := tc.exporter.SendAndWait(bg, input) - if err != nil || !sent { - b.Errorf("send failed: %v: %v", sent, err) - } - } -} diff --git a/collector/exporter/otelarrowexporter/internal/arrow/grpcmock/credentials.go b/collector/exporter/otelarrowexporter/internal/arrow/grpcmock/credentials.go deleted file mode 100644 index c9ddc953..00000000 --- a/collector/exporter/otelarrowexporter/internal/arrow/grpcmock/credentials.go +++ /dev/null @@ -1,74 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: google.golang.org/grpc/credentials (interfaces: PerRPCCredentials) -// -// Generated by this command: -// -// mockgen -package grpcmock google.golang.org/grpc/credentials PerRPCCredentials -// - -// Package grpcmock is a generated GoMock package. -package grpcmock - -import ( - context "context" - reflect "reflect" - - gomock "go.uber.org/mock/gomock" -) - -// MockPerRPCCredentials is a mock of PerRPCCredentials interface. -type MockPerRPCCredentials struct { - ctrl *gomock.Controller - recorder *MockPerRPCCredentialsMockRecorder -} - -// MockPerRPCCredentialsMockRecorder is the mock recorder for MockPerRPCCredentials. -type MockPerRPCCredentialsMockRecorder struct { - mock *MockPerRPCCredentials -} - -// NewMockPerRPCCredentials creates a new mock instance. -func NewMockPerRPCCredentials(ctrl *gomock.Controller) *MockPerRPCCredentials { - mock := &MockPerRPCCredentials{ctrl: ctrl} - mock.recorder = &MockPerRPCCredentialsMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockPerRPCCredentials) EXPECT() *MockPerRPCCredentialsMockRecorder { - return m.recorder -} - -// GetRequestMetadata mocks base method. -func (m *MockPerRPCCredentials) GetRequestMetadata(arg0 context.Context, arg1 ...string) (map[string]string, error) { - m.ctrl.T.Helper() - varargs := []any{arg0} - for _, a := range arg1 { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "GetRequestMetadata", varargs...) - ret0, _ := ret[0].(map[string]string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetRequestMetadata indicates an expected call of GetRequestMetadata. -func (mr *MockPerRPCCredentialsMockRecorder) GetRequestMetadata(arg0 any, arg1 ...any) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0}, arg1...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRequestMetadata", reflect.TypeOf((*MockPerRPCCredentials)(nil).GetRequestMetadata), varargs...) -} - -// RequireTransportSecurity mocks base method. -func (m *MockPerRPCCredentials) RequireTransportSecurity() bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RequireTransportSecurity") - ret0, _ := ret[0].(bool) - return ret0 -} - -// RequireTransportSecurity indicates an expected call of RequireTransportSecurity. -func (mr *MockPerRPCCredentialsMockRecorder) RequireTransportSecurity() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequireTransportSecurity", reflect.TypeOf((*MockPerRPCCredentials)(nil).RequireTransportSecurity)) -} diff --git a/collector/exporter/otelarrowexporter/internal/arrow/prioritizer.go b/collector/exporter/otelarrowexporter/internal/arrow/prioritizer.go deleted file mode 100644 index ac1675c0..00000000 --- a/collector/exporter/otelarrowexporter/internal/arrow/prioritizer.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package arrow // import "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow" - -import ( - "context" - "fmt" - "strconv" - "strings" - "time" - - "go.opentelemetry.io/collector/component" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -var ErrStreamRestarting = status.Error(codes.Aborted, "stream is restarting") - -type PrioritizerName string - -var _ component.ConfigValidator = PrioritizerName("") - -const ( - DefaultPrioritizer PrioritizerName = LeastLoadedPrioritizer - LeastLoadedPrioritizer PrioritizerName = llPrefix - LeastLoadedTwoPrioritizer PrioritizerName = llPrefix + "2" - LeastLoadedFourPrioritizer PrioritizerName = llPrefix + "4" - unsetPrioritizer PrioritizerName = "" - - llPrefix = "leastloaded" -) - -// streamPrioritizer is an interface for prioritizing multiple -// streams. -type streamPrioritizer interface { - // nextWriter gets the next stream writer. In case the exporter - // was downgraded, returns nil. - nextWriter() streamWriter - - // downgrade is called with the root context of the exporter, - // and may block indefinitely. this allows the prioritizer to - // drain its channel(s) until the exporter shuts down. - downgrade(context.Context) -} - -// streamWriter is the caller's interface to a stream. -type streamWriter interface { - // sendAndWait is called to begin a write. After completing - // the call, wait on writeItem.errCh for the response. - sendAndWait(context.Context, <-chan error, writeItem) error -} - -func newStreamPrioritizer(dc doneCancel, name PrioritizerName, numStreams int, maxLifetime time.Duration) (streamPrioritizer, []*streamWorkState) { - if name == unsetPrioritizer { - name = DefaultPrioritizer - } - if strings.HasPrefix(string(name), llPrefix) { - // error was checked and reported in Validate - n, err := strconv.Atoi(string(name[len(llPrefix):])) - if err == nil { - return newBestOfNPrioritizer(dc, n, numStreams, pendingRequests, maxLifetime) - } - } - return newBestOfNPrioritizer(dc, numStreams, numStreams, pendingRequests, maxLifetime) -} - -// pendingRequests is the load function used by leastloadedN. -func pendingRequests(sws *streamWorkState) float64 { - sws.lock.Lock() - defer sws.lock.Unlock() - return float64(len(sws.waiters) + len(sws.toWrite)) -} - -// Validate implements component.ConfigValidator -func (p PrioritizerName) Validate() error { - switch p { - // Exact match cases - case LeastLoadedPrioritizer, unsetPrioritizer: - return nil - } - // "leastloadedN" cases - if !strings.HasPrefix(string(p), llPrefix) { - return fmt.Errorf("unrecognized prioritizer: %q", string(p)) - } - _, err := strconv.Atoi(string(p[len(llPrefix):])) - if err != nil { - return fmt.Errorf("invalid prioritizer: %q", string(p)) - } - return nil -} - -// drain helps avoid a race condition when downgrade happens, it ensures that -// any late-arriving work will immediately see ErrStreamRestarting, and this -// continues until the exporter shuts down. -// -// Note: the downgrade function is a major source of complexity and it is -// probably best removed, instead of having this level of complexity. -func drain(ch <-chan writeItem, done <-chan struct{}) { - for { - select { - case <-done: - return - case item := <-ch: - item.errCh <- ErrStreamRestarting - } - } -} diff --git a/collector/exporter/otelarrowexporter/internal/arrow/stream.go b/collector/exporter/otelarrowexporter/internal/arrow/stream.go deleted file mode 100644 index 6142e5c0..00000000 --- a/collector/exporter/otelarrowexporter/internal/arrow/stream.go +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package arrow // import "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow" - -import ( - "bytes" - "context" - "errors" - "io" - "sync" - "time" - - arrowpb "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1" - "github.com/open-telemetry/otel-arrow/collector/netstats" - arrowRecord "github.com/open-telemetry/otel-arrow/pkg/otel/arrow_record" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/pdata/plog" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.opentelemetry.io/collector/pdata/ptrace" - "go.opentelemetry.io/otel" - otelcodes "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/propagation" - "go.opentelemetry.io/otel/trace" - "go.uber.org/multierr" - "go.uber.org/zap" - "golang.org/x/net/http2/hpack" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// Stream is 1:1 with gRPC stream. -type Stream struct { - // producer is exclusive to the holder of the stream. - producer arrowRecord.ProducerAPI - - // prioritizer has a reference to the stream, this allows it to be severed. - prioritizer streamPrioritizer - - // telemetry are a copy of the exporter's telemetry settings - telemetry component.TelemetrySettings - - // tracer is used to create a span describing the export. - tracer trace.Tracer - - // client uses the exporter's grpc.ClientConn. this is - // initially nil only set when ArrowStream() calls meaning the - // endpoint recognizes OTel-Arrow. - client AnyStreamClient - - // method the gRPC method name, used for additional instrumentation. - method string - - // netReporter provides network-level metrics. - netReporter netstats.Interface - - // streamWorkState is the interface to prioritizer/balancer, contains - // outstanding request (by batch ID) and the write channel used by - // the stream. All of this state will be inherited by the successor - // stream. - workState *streamWorkState -} - -// streamWorkState contains the state assigned to an Arrow stream. When -// a stream shuts down, the work state is handed to the replacement stream. -type streamWorkState struct { - // toWrite is used to pass pending data between a caller, the - // prioritizer and a stream. - toWrite chan writeItem - - // maxStreamLifetime is a limit on duration for streams. A - // slight "jitter" is applied relative to this value on a - // per-stream basis. - maxStreamLifetime time.Duration - - // lock protects waiters - lock sync.Mutex - - // waiters is the response channel for each active batch. - waiters map[int64]chan<- error -} - -// writeItem is passed from the sender (a pipeline consumer) to the -// stream writer, which is not bound by the sender's context. -type writeItem struct { - // records is a ptrace.Traces, plog.Logs, or pmetric.Metrics - records any - // md is the caller's metadata, derived from its context. - md map[string]string - // errCh is used by the stream reader to unblock the sender - // to the stream side, this is a `chan<-`. to the send side, - // this is a `<-chan`. - errCh chan<- error - // uncompSize is computed by the appropriate sizer (in the - // caller's goroutine) - uncompSize int - // producerCtx is used for tracing purposes. - producerCtx context.Context -} - -// newStream constructs a stream -func newStream( - producer arrowRecord.ProducerAPI, - prioritizer streamPrioritizer, - telemetry component.TelemetrySettings, - netReporter netstats.Interface, - workState *streamWorkState, -) *Stream { - tracer := telemetry.TracerProvider.Tracer("otel-arrow-exporter") - return &Stream{ - producer: producer, - prioritizer: prioritizer, - telemetry: telemetry, - tracer: tracer, - netReporter: netReporter, - workState: workState, - } -} - -// setBatchChannel places a waiting consumer's batchID into the waiters map, where -// the stream reader may find it. -func (s *Stream) setBatchChannel(batchID int64, errCh chan<- error) { - s.workState.lock.Lock() - defer s.workState.lock.Unlock() - - s.workState.waiters[batchID] = errCh -} - -// logStreamError decides how to log an error. `where` indicates the -// error location, will be "reader" or "writer". -func (s *Stream) logStreamError(where string, err error) { - var code codes.Code - var msg string - // gRPC tends to supply status-wrapped errors, so we always - // unpack them. A wrapped Canceled code indicates intentional - // shutdown, which can be due to normal causes (EOF, e.g., - // max-stream-lifetime reached) or unusual causes (Canceled, - // e.g., because the other stream direction reached an error). - if status, ok := status.FromError(err); ok { - code = status.Code() - msg = status.Message() - } else if errors.Is(err, io.EOF) || errors.Is(err, context.Canceled) { - code = codes.Canceled - msg = err.Error() - } else { - code = codes.Internal - msg = err.Error() - } - if code == codes.Canceled { - s.telemetry.Logger.Debug("arrow stream shutdown", zap.String("message", msg), zap.String("where", where)) - } else { - s.telemetry.Logger.Error("arrow stream error", zap.Int("code", int(code)), zap.String("message", msg), zap.String("where", where)) - } -} - -// run blocks the calling goroutine while executing stream logic. run -// will return when the reader and writer are finished. errors will be logged. -func (s *Stream) run(ctx context.Context, dc doneCancel, streamClient StreamClientFunc, grpcOptions []grpc.CallOption) { - sc, method, err := streamClient(ctx, grpcOptions...) - if err != nil { - // Returning with stream.client == nil signals the - // lack of an Arrow stream endpoint. When all the - // streams return with .client == nil, the ready - // channel will be closed, which causes downgrade. - // - // Note: These are gRPC server internal errors and - // will cause downgrade to standard OTLP. These - // cannot be simulated by connecting to a gRPC server - // that does not support the ArrowStream service, with - // or without the WaitForReady flag set. In a real - // gRPC server the first Unimplemented code is - // generally delivered to the Recv() call below, so - // this code path is not taken for an ordinary downgrade. - s.telemetry.Logger.Error("cannot start arrow stream", zap.Error(err)) - return - } - // Setting .client != nil indicates that the endpoint was valid, - // streaming may start. When this stream finishes, it will be - // restarted. - s.method = method - s.client = sc - - // ww is used to wait for the writer. Since we wait for the writer, - // the writer's goroutine is not added to exporter waitgroup (e.wg). - var ww sync.WaitGroup - - var writeErr error - ww.Add(1) - go func() { - defer ww.Done() - writeErr = s.write(ctx) - if writeErr != nil { - dc.cancel() - } - }() - - // the result from read() is processed after cancel and wait, - // so we can set s.client = nil in case of a delayed Unimplemented. - err = s.read(ctx) - - // Wait for the writer to ensure that all waiters are known. - dc.cancel() - ww.Wait() - - if err != nil { - // This branch is reached with an unimplemented status - // with or without the WaitForReady flag. - if status, ok := status.FromError(err); ok && status.Code() == codes.Unimplemented { - // This (client == nil) signals the controller to - // downgrade when all streams have returned in that - // status. - // - // This is a special case because we reset s.client, - // which sets up a downgrade after the streams return. - s.client = nil - s.telemetry.Logger.Info("arrow is not supported", - zap.String("message", status.Message()), - ) - } else { - // All other cases, use the standard log handler. - s.logStreamError("reader", err) - } - } - if writeErr != nil { - s.logStreamError("writer", writeErr) - } - - s.workState.lock.Lock() - defer s.workState.lock.Unlock() - - // The reader and writer have both finished; respond to any - // outstanding waiters. - for _, ch := range s.workState.waiters { - // Note: the top-level OTLP exporter will retry. - ch <- ErrStreamRestarting - } - - s.workState.waiters = map[int64]chan<- error{} -} - -// write repeatedly places this stream into the next-available queue, then -// performs a blocking send(). This returns when the data is in the write buffer, -// the caller waiting on its error channel. -func (s *Stream) write(ctx context.Context) (retErr error) { - // always close the send channel when this function returns. - defer func() { _ = s.client.CloseSend() }() - - // headers are encoding using hpack, reusing a buffer on each call. - var hdrsBuf bytes.Buffer - hdrsEnc := hpack.NewEncoder(&hdrsBuf) - - var timerCh <-chan time.Time - if s.workState.maxStreamLifetime != 0 { - timer := time.NewTimer(s.workState.maxStreamLifetime) - timerCh = timer.C - defer timer.Stop() - } - - for { - // this can block, and if the context is canceled we - // wait for the reader to find this stream. - var wri writeItem - select { - case <-timerCh: - return nil - case wri = <-s.workState.toWrite: - case <-ctx.Done(): - return status.Errorf(codes.Canceled, "stream input: %v", ctx.Err()) - } - - err := s.encodeAndSend(wri, &hdrsBuf, hdrsEnc) - if err != nil { - // Note: For the return statement below, there is no potential - // sender race because the stream is not available, as indicated by - // the successful <-stream.toWrite above - return err - } - } -} - -func (s *Stream) encodeAndSend(wri writeItem, hdrsBuf *bytes.Buffer, hdrsEnc *hpack.Encoder) (retErr error) { - ctx, span := s.tracer.Start(wri.producerCtx, "otel_arrow_stream_send") - defer span.End() - - defer func() { - // Set span status if an error is returned. - if retErr != nil { - span := trace.SpanFromContext(ctx) - span.SetStatus(otelcodes.Error, retErr.Error()) - } - }() - // Get the global propagator, to inject context. When there - // are no fields, it's a no-op propagator implementation and - // we can skip the allocations inside this block. - prop := otel.GetTextMapPropagator() - - if len(prop.Fields()) > 0 { - // When the incoming context carries nothing, the map - // will be nil. Allocate, if necessary. - if wri.md == nil { - wri.md = map[string]string{} - } - // Use the global propagator to inject trace context. Note that - // OpenTelemetry Collector will set a global propagator from the - // service::telemetry::traces configuration. - otel.GetTextMapPropagator().Inject(ctx, propagation.MapCarrier(wri.md)) - } - - batch, err := s.encode(wri.records) - if err != nil { - // This is some kind of internal error. We will restart the - // stream and mark this record as a permanent one. - err = status.Errorf(codes.Internal, "encode: %v", err) - wri.errCh <- err - return err - } - - // Optionally include outgoing metadata, if present. - if len(wri.md) != 0 { - hdrsBuf.Reset() - for key, val := range wri.md { - err := hdrsEnc.WriteField(hpack.HeaderField{ - Name: key, - Value: val, - }) - if err != nil { - // This case is like the encode-failure case - // above, we will restart the stream but consider - // this a permenent error. - err = status.Errorf(codes.Internal, "hpack: %v", err) - wri.errCh <- err - return err - } - } - batch.Headers = hdrsBuf.Bytes() - } - - // Let the receiver knows what to look for. - s.setBatchChannel(batch.BatchId, wri.errCh) - - // The netstats code knows that uncompressed size is - // unreliable for arrow transport, so we instrument it - // directly here. Only the primary direction of transport - // is instrumented this way. - var sized netstats.SizesStruct - sized.Method = s.method - sized.Length = int64(wri.uncompSize) - s.netReporter.CountSend(ctx, sized) - - if err := s.client.Send(batch); err != nil { - // The error will be sent to errCh during cleanup for this stream. - // Note: do not wrap this error, it may contain a Status. - return err - } - - return nil -} - -// read repeatedly reads a batch status and releases the consumers waiting for -// a response. -func (s *Stream) read(_ context.Context) error { - // Note we do not use the context, the stream context might - // cancel a call to Recv() but the call to processBatchStatus - // is non-blocking. - for { - // Note: if the client has called CloseSend() and is waiting for a response from the server. - // And if the server fails for some reason, we will wait until some other condition, such as a context - // timeout. TODO: possibly, improve to wait for no outstanding requests and then stop reading. - resp, err := s.client.Recv() - if err != nil { - // Note: do not wrap, contains a Status. - return err - } - - if err = s.processBatchStatus(resp); err != nil { - return err - } - } -} - -// getSenderChannel takes the stream lock and removes the corresonding -// sender channel. -func (sws *streamWorkState) getSenderChannel(bstat *arrowpb.BatchStatus) (chan<- error, error) { - sws.lock.Lock() - defer sws.lock.Unlock() - - ch, ok := sws.waiters[bstat.BatchId] - if !ok { - // Will break the stream. - return nil, status.Errorf(codes.Internal, "unrecognized batch ID: %d", bstat.BatchId) - } - - delete(sws.waiters, bstat.BatchId) - return ch, nil -} - -// processBatchStatus processes a single response from the server and unblocks the -// associated sender. -func (s *Stream) processBatchStatus(ss *arrowpb.BatchStatus) error { - ch, ret := s.workState.getSenderChannel(ss) - - if ch == nil { - // In case getSenderChannels encounters a problem, the - // channel is nil. - return ret - } - - if ss.StatusCode == arrowpb.StatusCode_OK { - ch <- nil - return nil - } - // See ../../otelarrow.go's `shouldRetry()` method, the retry - // behavior described here is achieved there by setting these - // recognized codes. - var err error - switch ss.StatusCode { - case arrowpb.StatusCode_UNAVAILABLE: - // Retryable - err = status.Errorf(codes.Unavailable, "destination unavailable: %d: %s", ss.BatchId, ss.StatusMessage) - case arrowpb.StatusCode_INVALID_ARGUMENT: - // Not retryable - err = status.Errorf(codes.InvalidArgument, "invalid argument: %d: %s", ss.BatchId, ss.StatusMessage) - case arrowpb.StatusCode_RESOURCE_EXHAUSTED: - // Retry behavior is configurable - err = status.Errorf(codes.ResourceExhausted, "resource exhausted: %d: %s", ss.BatchId, ss.StatusMessage) - default: - // Note: a Canceled StatusCode was once returned by receivers following - // a CloseSend() from the exporter. This is now handled using error - // status codes. If an exporter is upgraded before a receiver, the exporter - // will log this error when the receiver closes streams. - - // Unrecognized status code. - err = status.Errorf(codes.Internal, "unexpected stream response: %d: %s", ss.BatchId, ss.StatusMessage) - - // Will break the stream. - ret = multierr.Append(ret, err) - } - ch <- err - return ret -} - -// encode produces the next batch of Arrow records. -func (s *Stream) encode(records any) (_ *arrowpb.BatchArrowRecords, retErr error) { - // Defensively, protect against panics in the Arrow producer function. - defer func() { - if err := recover(); err != nil { - // When this happens, the stacktrace is - // important and lost if we don't capture it - // here. - s.telemetry.Logger.Debug("panic detail in otel-arrow-adapter", - zap.Reflect("recovered", err), - zap.Stack("stacktrace"), - ) - retErr = status.Errorf(codes.Internal, "panic in otel-arrow-adapter: %v", err) - } - }() - var batch *arrowpb.BatchArrowRecords - var err error - switch data := records.(type) { - case ptrace.Traces: - batch, err = s.producer.BatchArrowRecordsFromTraces(data) - case plog.Logs: - batch, err = s.producer.BatchArrowRecordsFromLogs(data) - case pmetric.Metrics: - batch, err = s.producer.BatchArrowRecordsFromMetrics(data) - default: - return nil, status.Errorf(codes.Unimplemented, "unsupported OTel-Arrow signal type: %T", records) - } - return batch, err -} diff --git a/collector/exporter/otelarrowexporter/internal/arrow/stream_test.go b/collector/exporter/otelarrowexporter/internal/arrow/stream_test.go deleted file mode 100644 index 416f8086..00000000 --- a/collector/exporter/otelarrowexporter/internal/arrow/stream_test.go +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package arrow - -import ( - "context" - "errors" - "fmt" - "sync" - "testing" - "time" - - arrowpb "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1" - "github.com/open-telemetry/otel-arrow/collector/netstats" - arrowRecordMock "github.com/open-telemetry/otel-arrow/pkg/otel/arrow_record/mock" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -var oneBatch = &arrowpb.BatchArrowRecords{ - BatchId: 1, -} - -type streamTestCase struct { - *commonTestCase - *commonTestStream - - producer *arrowRecordMock.MockProducerAPI - prioritizer streamPrioritizer - bgctx context.Context - doneCancel - fromTracesCall *gomock.Call - fromMetricsCall *gomock.Call - fromLogsCall *gomock.Call - stream *Stream - wait sync.WaitGroup -} - -func newStreamTestCase(t *testing.T, pname PrioritizerName) *streamTestCase { - ctrl := gomock.NewController(t) - producer := arrowRecordMock.NewMockProducerAPI(ctrl) - - bg, dc := newDoneCancel(context.Background()) - prio, state := newStreamPrioritizer(dc, pname, 1, 10*time.Second) - - ctc := newCommonTestCase(t, NotNoisy) - cts := ctc.newMockStream(bg) - - // metadata functionality is tested in exporter_test.go - ctc.requestMetadataCall.AnyTimes().Return(nil, nil) - - stream := newStream(producer, prio, ctc.telset, netstats.Noop{}, state[0]) - - fromTracesCall := producer.EXPECT().BatchArrowRecordsFromTraces(gomock.Any()).Times(0) - fromMetricsCall := producer.EXPECT().BatchArrowRecordsFromMetrics(gomock.Any()).Times(0) - fromLogsCall := producer.EXPECT().BatchArrowRecordsFromLogs(gomock.Any()).Times(0) - - return &streamTestCase{ - commonTestCase: ctc, - commonTestStream: cts, - producer: producer, - prioritizer: prio, - bgctx: bg, - doneCancel: dc, - stream: stream, - fromTracesCall: fromTracesCall, - fromMetricsCall: fromMetricsCall, - fromLogsCall: fromLogsCall, - } -} - -// start runs a test stream according to the behavior of testChannel. -func (tc *streamTestCase) start(channel testChannel) { - tc.traceCall.Times(1).DoAndReturn(tc.connectTestStream(channel)) - - tc.wait.Add(1) - - go func() { - defer tc.wait.Done() - tc.stream.run(tc.bgctx, tc.doneCancel, tc.traceClient, nil) - }() -} - -// cancelAndWait cancels the context and waits for the runner to return. -func (tc *streamTestCase) cancelAndWaitForShutdown() { - tc.cancel() - tc.wait.Wait() -} - -// cancel waits for the runner to exit without canceling the context. -func (tc *streamTestCase) waitForShutdown() { - tc.wait.Wait() -} - -// connectTestStream returns the stream under test from the common test's mock ArrowStream(). -func (tc *streamTestCase) connectTestStream(h testChannel) func(context.Context, ...grpc.CallOption) ( - arrowpb.ArrowTracesService_ArrowTracesClient, - error, -) { - return func(ctx context.Context, _ ...grpc.CallOption) ( - arrowpb.ArrowTracesService_ArrowTracesClient, - error, - ) { - if err := h.onConnect(ctx); err != nil { - return nil, err - } - tc.sendCall.AnyTimes().DoAndReturn(h.onSend(ctx)) - tc.recvCall.AnyTimes().DoAndReturn(h.onRecv(ctx)) - tc.closeSendCall.AnyTimes().DoAndReturn(h.onCloseSend()) - return tc.anyStreamClient, nil - } -} - -// get returns the stream via the prioritizer it is registered with. -func (tc *streamTestCase) mustGet() streamWriter { - stream := tc.prioritizer.nextWriter() - if stream == nil { - panic("unexpected nil stream") - } - return stream -} - -func (tc *streamTestCase) mustSendAndWait() error { - ctx := context.Background() - ch := make(chan error, 1) - wri := writeItem{ - producerCtx: context.Background(), - records: twoTraces, - errCh: ch, - } - return tc.mustGet().sendAndWait(ctx, ch, wri) -} - -// TestStreamNoMaxLifetime verifies that configuring -// max_stream_lifetime==0 works and the client never -// calls CloseSend(). -func TestStreamNoMaxLifetime(t *testing.T) { - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - - tc := newStreamTestCase(t, pname) - - tc.fromTracesCall.Times(1).Return(oneBatch, nil) - tc.closeSendCall.Times(0) - - channel := newHealthyTestChannel() - tc.start(channel) - defer tc.cancelAndWaitForShutdown() - var wg sync.WaitGroup - wg.Add(1) - defer wg.Wait() - go func() { - defer wg.Done() - batch := <-channel.sent - channel.recv <- statusOKFor(batch.BatchId) - }() - - err := tc.mustSendAndWait() - require.NoError(t, err) - }) - } -} - -// TestStreamEncodeError verifies that an encoder error in the sender -// yields a permanent error. -func TestStreamEncodeError(t *testing.T) { - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - tc := newStreamTestCase(t, pname) - - testErr := fmt.Errorf("test encode error") - tc.fromTracesCall.Times(1).Return(nil, testErr) - - tc.start(newHealthyTestChannel()) - defer tc.cancelAndWaitForShutdown() - - // sender should get a permanent testErr - err := tc.mustSendAndWait() - require.Error(t, err) - - stat, is := status.FromError(err) - require.True(t, is, "is a gRPC status error: %v", err) - require.Equal(t, codes.Internal, stat.Code()) - - require.Contains(t, stat.Message(), testErr.Error()) - }) - } -} - -// TestStreamUnknownBatchError verifies that the stream reader handles -// a unknown BatchID. -func TestStreamUnknownBatchError(t *testing.T) { - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - tc := newStreamTestCase(t, pname) - - tc.fromTracesCall.Times(1).Return(oneBatch, nil) - - channel := newHealthyTestChannel() - tc.start(channel) - defer tc.cancelAndWaitForShutdown() - - var wg sync.WaitGroup - wg.Add(1) - defer wg.Wait() - go func() { - defer wg.Done() - <-channel.sent - channel.recv <- statusOKFor(-1 /*unknown*/) - }() - // sender should get ErrStreamRestarting - err := tc.mustSendAndWait() - require.Error(t, err) - require.True(t, errors.Is(err, ErrStreamRestarting)) - }) - } -} - -// TestStreamStatusUnavailableInvalid verifies that the stream reader handles -// an unavailable or invalid status w/o breaking the stream. -func TestStreamStatusUnavailableInvalid(t *testing.T) { - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - tc := newStreamTestCase(t, pname) - - tc.fromTracesCall.Times(3).Return(oneBatch, nil) - - channel := newHealthyTestChannel() - tc.start(channel) - defer tc.cancelAndWaitForShutdown() - - var wg sync.WaitGroup - wg.Add(1) - defer wg.Wait() - go func() { - defer wg.Done() - batch := <-channel.sent - channel.recv <- statusUnavailableFor(batch.BatchId) - batch = <-channel.sent - channel.recv <- statusInvalidFor(batch.BatchId) - batch = <-channel.sent - channel.recv <- statusOKFor(batch.BatchId) - }() - // sender should get "test unavailable" once, success second time. - err := tc.mustSendAndWait() - require.Error(t, err) - require.Contains(t, err.Error(), "test unavailable") - - err = tc.mustSendAndWait() - require.Error(t, err) - require.Contains(t, err.Error(), "test invalid") - - err = tc.mustSendAndWait() - require.NoError(t, err) - }) - } -} - -// TestStreamStatusUnrecognized verifies that the stream reader handles -// an unrecognized status by breaking the stream. -func TestStreamStatusUnrecognized(t *testing.T) { - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - tc := newStreamTestCase(t, pname) - - tc.fromTracesCall.Times(1).Return(oneBatch, nil) - - channel := newHealthyTestChannel() - tc.start(channel) - defer tc.cancelAndWaitForShutdown() - - var wg sync.WaitGroup - wg.Add(1) - defer wg.Wait() - go func() { - defer wg.Done() - batch := <-channel.sent - channel.recv <- statusUnrecognizedFor(batch.BatchId) - }() - err := tc.mustSendAndWait() - require.Error(t, err) - require.Contains(t, err.Error(), "test unrecognized") - - // Note: do not cancel the context, the stream should be - // shutting down due to the error. - tc.waitForShutdown() - }) - } -} - -// TestStreamUnsupported verifies that the stream signals downgrade -// when an Unsupported code is received, which is how the gRPC client -// responds when the server does not support arrow. -func TestStreamUnsupported(t *testing.T) { - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - tc := newStreamTestCase(t, pname) - - // If the write succeeds before the read, then the FromTraces - // call will occur. Otherwise, it will not. - - tc.fromTracesCall.MinTimes(0).MaxTimes(1).Return(oneBatch, nil) - - channel := newArrowUnsupportedTestChannel() - tc.start(channel) - defer func() { - // When the stream returns, the downgrade is needed to - // cause the request to respond or else it waits for a new - // stream. - tc.waitForShutdown() - tc.cancel() - }() - - err := tc.mustSendAndWait() - require.Equal(t, ErrStreamRestarting, err) - - tc.waitForShutdown() - - require.Less(t, 0, len(tc.observedLogs.All()), "should have at least one log: %v", tc.observedLogs.All()) - require.Equal(t, tc.observedLogs.All()[0].Message, "arrow is not supported") - }) - } -} - -// TestStreamSendError verifies that the stream reader handles a -// Send() error. -func TestStreamSendError(t *testing.T) { - for _, pname := range AllPrioritizers { - t.Run(string(pname), func(t *testing.T) { - tc := newStreamTestCase(t, pname) - - tc.fromTracesCall.Times(1).Return(oneBatch, nil) - - channel := newSendErrorTestChannel() - tc.start(channel) - defer tc.cancelAndWaitForShutdown() - - go func() { - time.Sleep(200 * time.Millisecond) - channel.unblock() - }() - // sender should get ErrStreamRestarting - err := tc.mustSendAndWait() - require.Error(t, err) - require.True(t, errors.Is(err, ErrStreamRestarting)) - }) - } -} diff --git a/collector/exporter/otelarrowexporter/internal/metadata/generated_status.go b/collector/exporter/otelarrowexporter/internal/metadata/generated_status.go deleted file mode 100644 index fb81886d..00000000 --- a/collector/exporter/otelarrowexporter/internal/metadata/generated_status.go +++ /dev/null @@ -1,17 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package metadata - -import ( - "go.opentelemetry.io/collector/component" -) - -var ( - Type = component.MustNewType("otelarrow") -) - -const ( - TracesStability = component.StabilityLevelDevelopment - MetricsStability = component.StabilityLevelDevelopment - LogsStability = component.StabilityLevelDevelopment -) diff --git a/collector/exporter/otelarrowexporter/metadata.yaml b/collector/exporter/otelarrowexporter/metadata.yaml deleted file mode 100644 index 59ac44f0..00000000 --- a/collector/exporter/otelarrowexporter/metadata.yaml +++ /dev/null @@ -1,15 +0,0 @@ -type: otelarrow -scope_name: otelcol/otelarrow - -status: - class: exporter - stability: - development: [traces, metrics, logs] - distributions: [] - codeowners: - active: [jmacd, moh-osman3] - -# TODO: Update the exporter to pass the tests -tests: - skip_lifecycle: true - skip_shutdown: true diff --git a/collector/exporter/otelarrowexporter/otelarrow.go b/collector/exporter/otelarrowexporter/otelarrow.go deleted file mode 100644 index 45a1b439..00000000 --- a/collector/exporter/otelarrowexporter/otelarrow.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowexporter // import "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter" - -import ( - "context" - "errors" - "fmt" - "runtime" - "time" - - arrowPkg "github.com/apache/arrow/go/v16/arrow" - "github.com/open-telemetry/otel-arrow/collector/compression/zstd" - "github.com/open-telemetry/otel-arrow/collector/netstats" - arrowRecord "github.com/open-telemetry/otel-arrow/pkg/otel/arrow_record" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configcompression" - "go.opentelemetry.io/collector/consumer/consumererror" - "go.opentelemetry.io/collector/exporter" - "go.opentelemetry.io/collector/exporter/exporterhelper" - "go.opentelemetry.io/collector/pdata/plog" - "go.opentelemetry.io/collector/pdata/plog/plogotlp" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" - "go.opentelemetry.io/collector/pdata/ptrace" - "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" - "go.uber.org/multierr" - "go.uber.org/zap" - "google.golang.org/genproto/googleapis/rpc/errdetails" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - - "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow" -) - -type baseExporter struct { - // Input configuration. - config *Config - - // gRPC clients and connection. - traceExporter ptraceotlp.GRPCClient - metricExporter pmetricotlp.GRPCClient - logExporter plogotlp.GRPCClient - clientConn *grpc.ClientConn - metadata metadata.MD - callOptions []grpc.CallOption - settings exporter.Settings - netReporter *netstats.NetworkReporter - - // Default user-agent header. - userAgent string - - // OTel-Arrow optional state - arrow *arrow.Exporter - // streamClientFunc is the stream constructor - streamClientFactory streamClientFactory -} - -type streamClientFactory func(conn *grpc.ClientConn) arrow.StreamClientFunc - -// Crete new exporter and start it. The exporter will begin connecting but -// this function may return before the connection is established. -func newExporter(cfg component.Config, set exporter.Settings, streamClientFactory streamClientFactory) (*baseExporter, error) { - oCfg := cfg.(*Config) - - if oCfg.Endpoint == "" { - return nil, errors.New("OTLP exporter config requires an Endpoint") - } - - netReporter, err := netstats.NewExporterNetworkReporter(set) - if err != nil { - return nil, err - } - userAgent := fmt.Sprintf("%s/%s (%s/%s)", - set.BuildInfo.Description, set.BuildInfo.Version, runtime.GOOS, runtime.GOARCH) - - if !oCfg.Arrow.Disabled { - // Ignoring an error because Validate() was called. - _ = zstd.SetEncoderConfig(oCfg.Arrow.Zstd) - - userAgent += fmt.Sprintf(" ApacheArrow/%s (NumStreams/%d)", arrowPkg.PkgVersion, oCfg.Arrow.NumStreams) - } - - return &baseExporter{ - config: oCfg, - settings: set, - userAgent: userAgent, - netReporter: netReporter, - streamClientFactory: streamClientFactory, - }, nil -} - -// start actually creates the gRPC connection. The client construction is deferred till this point as this -// is the only place we get hold of Extensions which are required to construct auth round tripper. -func (e *baseExporter) start(ctx context.Context, host component.Host) (err error) { - dialOpts := []grpc.DialOption{ - grpc.WithUserAgent(e.userAgent), - } - if e.netReporter != nil { - dialOpts = append(dialOpts, grpc.WithStatsHandler(e.netReporter.Handler())) - } - dialOpts = append(dialOpts, e.config.UserDialOptions...) - if e.clientConn, err = e.config.ClientConfig.ToClientConn(ctx, host, e.settings.TelemetrySettings, dialOpts...); err != nil { - return err - } - e.traceExporter = ptraceotlp.NewGRPCClient(e.clientConn) - e.metricExporter = pmetricotlp.NewGRPCClient(e.clientConn) - e.logExporter = plogotlp.NewGRPCClient(e.clientConn) - headers := map[string]string{} - for k, v := range e.config.ClientConfig.Headers { - headers[k] = string(v) - } - e.metadata = metadata.New(headers) - e.callOptions = []grpc.CallOption{ - grpc.WaitForReady(e.config.ClientConfig.WaitForReady), - } - - if !e.config.Arrow.Disabled { - // Note this sets static outgoing context for all future stream requests. - ctx := e.enhanceContext(context.Background()) - - var perRPCCreds credentials.PerRPCCredentials - if e.config.ClientConfig.Auth != nil { - // Get the auth extension, we'll use it to enrich the request context. - authClient, err := e.config.ClientConfig.Auth.GetClientAuthenticatorContext(ctx, host.GetExtensions()) - if err != nil { - return err - } - - perRPCCreds, err = authClient.PerRPCCredentials() - if err != nil { - return err - } - } - - arrowOpts := e.config.Arrow.toArrowProducerOptions() - - arrowCallOpts := e.callOptions - - if e.config.ClientConfig.Compression == configcompression.TypeZstd { - // ignore the error below b/c Validate() was called - _ = zstd.SetEncoderConfig(e.config.Arrow.Zstd) - // use the configured compressor. - arrowCallOpts = append(arrowCallOpts, e.config.Arrow.Zstd.CallOption()) - } - - e.arrow = arrow.NewExporter(e.config.Arrow.MaxStreamLifetime, e.config.Arrow.NumStreams, e.config.Arrow.Prioritizer, e.config.Arrow.DisableDowngrade, e.settings.TelemetrySettings, arrowCallOpts, func() arrowRecord.ProducerAPI { - return arrowRecord.NewProducerWithOptions(arrowOpts...) - }, e.streamClientFactory(e.clientConn), perRPCCreds, e.netReporter) - - if err := e.arrow.Start(ctx); err != nil { - return err - } - } - - return nil -} - -func (e *baseExporter) shutdown(ctx context.Context) error { - var err error - if e.arrow != nil { - err = multierr.Append(err, e.arrow.Shutdown(ctx)) - } - if e.clientConn != nil { - err = multierr.Append(err, e.clientConn.Close()) - } - return err -} - -// arrowSendAndWait gets an available stream and tries to send using -// Arrow if it is configured. A (false, nil) result indicates for the -// caller to fall back to ordinary OTLP. -// -// Note that ctx is has not had enhanceContext() called, meaning it -// will have outgoing gRPC metadata only when an upstream processor or -// receiver placed it there. -func (e *baseExporter) arrowSendAndWait(ctx context.Context, data any) (sent bool, _ error) { - if e.arrow == nil { - return false, nil - } - sent, err := e.arrow.SendAndWait(ctx, data) - if err != nil { - return sent, processError(err) - } - return sent, nil -} - -func (e *baseExporter) pushTraces(ctx context.Context, td ptrace.Traces) error { - if sent, err := e.arrowSendAndWait(ctx, td); err != nil { - return err - } else if sent { - return nil - } - req := ptraceotlp.NewExportRequestFromTraces(td) - resp, respErr := e.traceExporter.Export(e.enhanceContext(ctx), req, e.callOptions...) - if err := processError(respErr); err != nil { - return err - } - partialSuccess := resp.PartialSuccess() - if !(partialSuccess.ErrorMessage() == "" && partialSuccess.RejectedSpans() == 0) { - // TODO: These should be counted, similar to dropped items. - e.settings.Logger.Warn("partial success", - zap.String("message", resp.PartialSuccess().ErrorMessage()), - zap.Int64("num_rejected", resp.PartialSuccess().RejectedSpans()), - ) - } - return nil -} - -func (e *baseExporter) pushMetrics(ctx context.Context, md pmetric.Metrics) error { - if sent, err := e.arrowSendAndWait(ctx, md); err != nil { - return err - } else if sent { - return nil - } - req := pmetricotlp.NewExportRequestFromMetrics(md) - resp, respErr := e.metricExporter.Export(e.enhanceContext(ctx), req, e.callOptions...) - if err := processError(respErr); err != nil { - return err - } - partialSuccess := resp.PartialSuccess() - if !(partialSuccess.ErrorMessage() == "" && partialSuccess.RejectedDataPoints() == 0) { - // TODO: These should be counted, similar to dropped items. - e.settings.Logger.Warn("partial success", - zap.String("message", resp.PartialSuccess().ErrorMessage()), - zap.Int64("num_rejected", resp.PartialSuccess().RejectedDataPoints()), - ) - } - return nil -} - -func (e *baseExporter) pushLogs(ctx context.Context, ld plog.Logs) error { - if sent, err := e.arrowSendAndWait(ctx, ld); err != nil { - return err - } else if sent { - return nil - } - req := plogotlp.NewExportRequestFromLogs(ld) - resp, respErr := e.logExporter.Export(e.enhanceContext(ctx), req, e.callOptions...) - if err := processError(respErr); err != nil { - return err - } - partialSuccess := resp.PartialSuccess() - if !(partialSuccess.ErrorMessage() == "" && partialSuccess.RejectedLogRecords() == 0) { - // TODO: These should be counted, similar to dropped items. - e.settings.Logger.Warn("partial success", - zap.String("message", resp.PartialSuccess().ErrorMessage()), - zap.Int64("num_rejected", resp.PartialSuccess().RejectedLogRecords()), - ) - } - return nil -} - -func (e *baseExporter) enhanceContext(ctx context.Context) context.Context { - if e.metadata.Len() > 0 { - ctx = metadata.NewOutgoingContext(ctx, e.metadata) - } - return ctx -} - -func processError(err error) error { - if err == nil { - // Request is successful, we are done. - return nil - } - - // We have an error, check gRPC status code. - st := status.Convert(err) - if st.Code() == codes.OK { - // Not really an error, still success. - return nil - } - - // Now, this is this a real error. - - retryInfo := getRetryInfo(st) - - if !shouldRetry(st.Code(), retryInfo) { - // It is not a retryable error, we should not retry. - return consumererror.NewPermanent(err) - } - - // Check if server returned throttling information. - throttleDuration := getThrottleDuration(retryInfo) - if throttleDuration != 0 { - // We are throttled. Wait before retrying as requested by the server. - return exporterhelper.NewThrottleRetry(err, throttleDuration) - } - - // Need to retry. - - return err -} - -func shouldRetry(code codes.Code, retryInfo *errdetails.RetryInfo) bool { - switch code { - case codes.Canceled, - codes.DeadlineExceeded, - codes.Aborted, - codes.OutOfRange, - codes.Unavailable, - codes.DataLoss: - // These are retryable errors. - return true - case codes.ResourceExhausted: - // Retry only if RetryInfo was supplied by the server. - // This indicates that the server can still recover from resource exhaustion. - return retryInfo != nil - } - // Don't retry on any other code. - return false -} - -func getRetryInfo(status *status.Status) *errdetails.RetryInfo { - for _, detail := range status.Details() { - if t, ok := detail.(*errdetails.RetryInfo); ok { - return t - } - } - return nil -} - -func getThrottleDuration(t *errdetails.RetryInfo) time.Duration { - if t == nil || t.RetryDelay == nil { - return 0 - } - if t.RetryDelay.Seconds > 0 || t.RetryDelay.Nanos > 0 { - return time.Duration(t.RetryDelay.Seconds)*time.Second + time.Duration(t.RetryDelay.Nanos)*time.Nanosecond - } - return 0 -} diff --git a/collector/exporter/otelarrowexporter/otelarrow_test.go b/collector/exporter/otelarrowexporter/otelarrow_test.go deleted file mode 100644 index 01104918..00000000 --- a/collector/exporter/otelarrowexporter/otelarrow_test.go +++ /dev/null @@ -1,1191 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowexporter - -import ( - "context" - "fmt" - "io" - "net" - "net/http" - "path/filepath" - "runtime" - "sync" - "sync/atomic" - "testing" - "time" - - arrowpb "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1" - arrowpbMock "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1/mock" - "github.com/open-telemetry/otel-arrow/collector/testdata" - arrowRecord "github.com/open-telemetry/otel-arrow/pkg/otel/arrow_record" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/client" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/config/configauth" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/config/configopaque" - "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/exporter" - "go.opentelemetry.io/collector/exporter/exportertest" - "go.opentelemetry.io/collector/extension" - "go.opentelemetry.io/collector/extension/auth" - "go.opentelemetry.io/collector/pdata/plog" - "go.opentelemetry.io/collector/pdata/plog/plogotlp" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" - "go.opentelemetry.io/collector/pdata/ptrace" - "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" - "go.uber.org/mock/gomock" - "go.uber.org/zap/zaptest" - "golang.org/x/net/http2/hpack" - "google.golang.org/genproto/googleapis/rpc/errdetails" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/durationpb" - - "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter/internal/arrow/grpcmock" -) - -type mockReceiver struct { - srv *grpc.Server - ln net.Listener - requestCount *atomic.Int32 - totalItems *atomic.Int32 - mux sync.Mutex - metadata metadata.MD - exportError error -} - -func (r *mockReceiver) getMetadata() metadata.MD { - r.mux.Lock() - defer r.mux.Unlock() - return r.metadata -} - -func (r *mockReceiver) setExportError(err error) { - r.mux.Lock() - defer r.mux.Unlock() - r.exportError = err -} - -type mockTracesReceiver struct { - ptraceotlp.UnimplementedGRPCServer - mockReceiver - exportResponse func() ptraceotlp.ExportResponse - lastRequest ptrace.Traces -} - -func (r *mockTracesReceiver) Export(ctx context.Context, req ptraceotlp.ExportRequest) (ptraceotlp.ExportResponse, error) { - r.requestCount.Add(int32(1)) - td := req.Traces() - r.totalItems.Add(int32(td.SpanCount())) - r.mux.Lock() - defer r.mux.Unlock() - r.lastRequest = td - r.metadata, _ = metadata.FromIncomingContext(ctx) - return r.exportResponse(), r.exportError -} - -func (r *mockTracesReceiver) getLastRequest() ptrace.Traces { - r.mux.Lock() - defer r.mux.Unlock() - return r.lastRequest -} - -func (r *mockTracesReceiver) setExportResponse(fn func() ptraceotlp.ExportResponse) { - r.mux.Lock() - defer r.mux.Unlock() - r.exportResponse = fn -} - -func otelArrowTracesReceiverOnGRPCServer(ln net.Listener, useTLS bool) (*mockTracesReceiver, error) { - sopts := []grpc.ServerOption{} - - if useTLS { - _, currentFile, _, _ := runtime.Caller(0) - basepath := filepath.Dir(currentFile) - certpath := filepath.Join(basepath, filepath.Join("testdata", "test_cert.pem")) - keypath := filepath.Join(basepath, filepath.Join("testdata", "test_key.pem")) - - creds, err := credentials.NewServerTLSFromFile(certpath, keypath) - if err != nil { - return nil, err - } - sopts = append(sopts, grpc.Creds(creds)) - } - - rcv := &mockTracesReceiver{ - mockReceiver: mockReceiver{ - srv: grpc.NewServer(sopts...), - ln: ln, - requestCount: &atomic.Int32{}, - totalItems: &atomic.Int32{}, - }, - exportResponse: ptraceotlp.NewExportResponse, - } - - ptraceotlp.RegisterGRPCServer(rcv.srv, rcv) - - return rcv, nil -} - -func (r *mockTracesReceiver) start() { - go func() { - _ = r.srv.Serve(r.ln) - }() -} - -type mockLogsReceiver struct { - plogotlp.UnimplementedGRPCServer - mockReceiver - exportResponse func() plogotlp.ExportResponse - lastRequest plog.Logs -} - -func (r *mockLogsReceiver) Export(ctx context.Context, req plogotlp.ExportRequest) (plogotlp.ExportResponse, error) { - r.requestCount.Add(int32(1)) - ld := req.Logs() - r.totalItems.Add(int32(ld.LogRecordCount())) - r.mux.Lock() - defer r.mux.Unlock() - r.lastRequest = ld - r.metadata, _ = metadata.FromIncomingContext(ctx) - return r.exportResponse(), r.exportError -} - -func (r *mockLogsReceiver) getLastRequest() plog.Logs { - r.mux.Lock() - defer r.mux.Unlock() - return r.lastRequest -} - -func (r *mockLogsReceiver) setExportResponse(fn func() plogotlp.ExportResponse) { - r.mux.Lock() - defer r.mux.Unlock() - r.exportResponse = fn -} - -func otelArrowLogsReceiverOnGRPCServer(ln net.Listener) *mockLogsReceiver { - rcv := &mockLogsReceiver{ - mockReceiver: mockReceiver{ - srv: grpc.NewServer(), - requestCount: &atomic.Int32{}, - totalItems: &atomic.Int32{}, - }, - exportResponse: plogotlp.NewExportResponse, - } - - // Now run it as a gRPC server - plogotlp.RegisterGRPCServer(rcv.srv, rcv) - go func() { - _ = rcv.srv.Serve(ln) - }() - - return rcv -} - -type mockMetricsReceiver struct { - pmetricotlp.UnimplementedGRPCServer - mockReceiver - exportResponse func() pmetricotlp.ExportResponse - lastRequest pmetric.Metrics -} - -func (r *mockMetricsReceiver) Export(ctx context.Context, req pmetricotlp.ExportRequest) (pmetricotlp.ExportResponse, error) { - md := req.Metrics() - r.requestCount.Add(int32(1)) - r.totalItems.Add(int32(md.DataPointCount())) - r.mux.Lock() - defer r.mux.Unlock() - r.lastRequest = md - r.metadata, _ = metadata.FromIncomingContext(ctx) - return r.exportResponse(), r.exportError -} - -func (r *mockMetricsReceiver) getLastRequest() pmetric.Metrics { - r.mux.Lock() - defer r.mux.Unlock() - return r.lastRequest -} - -func (r *mockMetricsReceiver) setExportResponse(fn func() pmetricotlp.ExportResponse) { - r.mux.Lock() - defer r.mux.Unlock() - r.exportResponse = fn -} - -func otelArrowMetricsReceiverOnGRPCServer(ln net.Listener) *mockMetricsReceiver { - rcv := &mockMetricsReceiver{ - mockReceiver: mockReceiver{ - srv: grpc.NewServer(), - requestCount: &atomic.Int32{}, - totalItems: &atomic.Int32{}, - }, - exportResponse: pmetricotlp.NewExportResponse, - } - - // Now run it as a gRPC server - pmetricotlp.RegisterGRPCServer(rcv.srv, rcv) - go func() { - _ = rcv.srv.Serve(ln) - }() - - return rcv -} - -type hostWithExtensions struct { - component.Host - exts map[component.ID]component.Component -} - -func newHostWithExtensions(exts map[component.ID]component.Component) component.Host { - return &hostWithExtensions{ - Host: componenttest.NewNopHost(), - exts: exts, - } -} - -func (h *hostWithExtensions) GetExtensions() map[component.ID]component.Component { - return h.exts -} - -type testAuthExtension struct { - extension.Extension - - prc credentials.PerRPCCredentials -} - -func newTestAuthExtension(t *testing.T, mdf func(ctx context.Context) map[string]string) auth.Client { - ctrl := gomock.NewController(t) - prc := grpcmock.NewMockPerRPCCredentials(ctrl) - prc.EXPECT().RequireTransportSecurity().AnyTimes().Return(false) - prc.EXPECT().GetRequestMetadata(gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn( - func(ctx context.Context, _ ...string) (map[string]string, error) { - return mdf(ctx), nil - }, - ) - return &testAuthExtension{ - prc: prc, - } -} - -func (a *testAuthExtension) RoundTripper(_ http.RoundTripper) (http.RoundTripper, error) { - return nil, fmt.Errorf("unused") -} - -func (a *testAuthExtension) PerRPCCredentials() (credentials.PerRPCCredentials, error) { - return a.prc, nil -} - -func TestSendTraces(t *testing.T) { - // Start an OTel-Arrow receiver. - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - rcv, _ := otelArrowTracesReceiverOnGRPCServer(ln, false) - rcv.start() - // Also closes the connection. - defer rcv.srv.GracefulStop() - - // Start an OTLP exporter and point to the receiver. - factory := NewFactory() - authID := component.NewID(component.MustNewType("testauth")) - expectedHeader := []string{"header-value"} - - cfg := factory.CreateDefaultConfig().(*Config) - // Disable queuing to ensure that we execute the request when calling ConsumeTraces - // otherwise we will not see any errors. - cfg.QueueSettings.Enabled = false - cfg.ClientConfig = configgrpc.ClientConfig{ - Endpoint: ln.Addr().String(), - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - Headers: map[string]configopaque.String{ - "header": configopaque.String(expectedHeader[0]), - }, - Auth: &configauth.Authentication{ - AuthenticatorID: authID, - }, - } - // This test fails w/ Arrow enabled because the function - // passed to newTestAuthExtension() below requires it the - // caller's context, and the newStream doesn't have it. - cfg.Arrow.Disabled = true - - set := exportertest.NewNopSettings() - set.BuildInfo.Description = "Collector" - set.BuildInfo.Version = "1.2.3test" - exp, err := factory.CreateTracesExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - - defer func() { - assert.NoError(t, exp.Shutdown(context.Background())) - }() - - host := newHostWithExtensions( - map[component.ID]component.Component{ - authID: newTestAuthExtension(t, func(ctx context.Context) map[string]string { - return map[string]string{ - "callerid": client.FromContext(ctx).Metadata.Get("in_callerid")[0], - } - }), - }, - ) - assert.NoError(t, exp.Start(context.Background(), host)) - - // Ensure that initially there is no data in the receiver. - assert.EqualValues(t, 0, rcv.requestCount.Load()) - - newCallerContext := func(value string) context.Context { - return client.NewContext(context.Background(), - client.Info{ - Metadata: client.NewMetadata(map[string][]string{ - "in_callerid": {value}, - }), - }, - ) - } - const caller1 = "caller1" - const caller2 = "caller2" - callCtx1 := newCallerContext(caller1) - callCtx2 := newCallerContext(caller2) - - // Send empty trace. - td := ptrace.NewTraces() - assert.NoError(t, exp.ConsumeTraces(callCtx1, td)) - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 0 - }, 10*time.Second, 5*time.Millisecond) - - // Ensure it was received empty. - assert.EqualValues(t, 0, rcv.totalItems.Load()) - md := rcv.getMetadata() - - // Expect caller1 and the static header - require.EqualValues(t, expectedHeader, md.Get("header")) - require.EqualValues(t, []string{caller1}, md.Get("callerid")) - - // A trace with 2 spans. - td = testdata.GenerateTraces(2) - - err = exp.ConsumeTraces(callCtx2, td) - assert.NoError(t, err) - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 1 - }, 10*time.Second, 5*time.Millisecond) - - // Verify received span. - assert.EqualValues(t, 2, rcv.totalItems.Load()) - assert.EqualValues(t, 2, rcv.requestCount.Load()) - assert.EqualValues(t, td, rcv.getLastRequest()) - - // Test the static metadata - md = rcv.getMetadata() - require.EqualValues(t, expectedHeader, md.Get("header")) - require.Equal(t, len(md.Get("User-Agent")), 1) - require.Contains(t, md.Get("User-Agent")[0], "Collector/1.2.3test") - - // Test the caller's dynamic metadata - require.EqualValues(t, []string{caller2}, md.Get("callerid")) - - // Return partial success - rcv.setExportResponse(func() ptraceotlp.ExportResponse { - response := ptraceotlp.NewExportResponse() - partialSuccess := response.PartialSuccess() - partialSuccess.SetErrorMessage("Some spans were not ingested") - partialSuccess.SetRejectedSpans(1) - - return response - }) - - // A request with 2 Trace entries. - td = testdata.GenerateTraces(2) - - // PartialSuccess is not an error. - err = exp.ConsumeTraces(callCtx1, td) - assert.NoError(t, err) -} - -func TestSendTracesWhenEndpointHasHttpScheme(t *testing.T) { - tests := []struct { - name string - useTLS bool - scheme string - gRPCClientSettings configgrpc.ClientConfig - }{ - { - name: "Use https scheme", - useTLS: true, - scheme: "https://", - gRPCClientSettings: configgrpc.ClientConfig{}, - }, - { - name: "Use http scheme", - useTLS: false, - scheme: "http://", - gRPCClientSettings: configgrpc.ClientConfig{ - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - // Start an OTel-Arrow receiver. - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - rcv, err := otelArrowTracesReceiverOnGRPCServer(ln, test.useTLS) - rcv.start() - require.NoError(t, err, "Failed to start mock OTLP receiver") - // Also closes the connection. - defer rcv.srv.GracefulStop() - - // Start an OTLP exporter and point to the receiver. - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.ClientConfig = test.gRPCClientSettings - cfg.ClientConfig.Endpoint = test.scheme + ln.Addr().String() - cfg.Arrow.MaxStreamLifetime = 100 * time.Second - if test.useTLS { - cfg.ClientConfig.TLSSetting.InsecureSkipVerify = true - } - set := exportertest.NewNopSettings() - exp, err := factory.CreateTracesExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - - defer func() { - assert.NoError(t, exp.Shutdown(context.Background())) - }() - - host := componenttest.NewNopHost() - assert.NoError(t, exp.Start(context.Background(), host)) - - // Ensure that initially there is no data in the receiver. - assert.EqualValues(t, 0, rcv.requestCount.Load()) - - // Send empty trace. - td := ptrace.NewTraces() - assert.NoError(t, exp.ConsumeTraces(context.Background(), td)) - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 0 - }, 10*time.Second, 5*time.Millisecond) - - // Ensure it was received empty. - assert.EqualValues(t, 0, rcv.totalItems.Load()) - }) - } -} - -func TestSendMetrics(t *testing.T) { - // Start an OTel-Arrow receiver. - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - rcv := otelArrowMetricsReceiverOnGRPCServer(ln) - // Also closes the connection. - defer rcv.srv.GracefulStop() - - // Start an OTLP exporter and point to the receiver. - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - // Disable queuing to ensure that we execute the request when calling ConsumeMetrics - // otherwise we will not see any errors. - cfg.QueueSettings.Enabled = false - cfg.RetryConfig.Enabled = false - cfg.ClientConfig = configgrpc.ClientConfig{ - Endpoint: ln.Addr().String(), - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - Headers: map[string]configopaque.String{ - "header": "header-value", - }, - } - cfg.Arrow.MaxStreamLifetime = 100 * time.Second - set := exportertest.NewNopSettings() - set.BuildInfo.Description = "Collector" - set.BuildInfo.Version = "1.2.3test" - exp, err := factory.CreateMetricsExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - defer func() { - assert.NoError(t, exp.Shutdown(context.Background())) - }() - - host := componenttest.NewNopHost() - - assert.NoError(t, exp.Start(context.Background(), host)) - - // Ensure that initially there is no data in the receiver. - assert.EqualValues(t, 0, rcv.requestCount.Load()) - - // Send empty metric. - md := pmetric.NewMetrics() - assert.NoError(t, exp.ConsumeMetrics(context.Background(), md)) - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 0 - }, 10*time.Second, 5*time.Millisecond) - - // Ensure it was received empty. - assert.EqualValues(t, 0, rcv.totalItems.Load()) - - // Send two metrics. - md = testdata.GenerateMetrics(2) - - err = exp.ConsumeMetrics(context.Background(), md) - assert.NoError(t, err) - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 1 - }, 10*time.Second, 5*time.Millisecond) - - expectedHeader := []string{"header-value"} - - // Verify received metrics. - assert.EqualValues(t, uint32(2), rcv.requestCount.Load()) - assert.EqualValues(t, uint32(4), rcv.totalItems.Load()) - assert.EqualValues(t, md, rcv.getLastRequest()) - - mdata := rcv.getMetadata() - require.EqualValues(t, mdata.Get("header"), expectedHeader) - require.Equal(t, len(mdata.Get("User-Agent")), 1) - require.Contains(t, mdata.Get("User-Agent")[0], "Collector/1.2.3test") - - st := status.New(codes.InvalidArgument, "Invalid argument") - rcv.setExportError(st.Err()) - - // Send two metrics.. - md = testdata.GenerateMetrics(2) - - err = exp.ConsumeMetrics(context.Background(), md) - assert.Error(t, err) - - rcv.setExportError(nil) - - // Return partial success - rcv.setExportResponse(func() pmetricotlp.ExportResponse { - response := pmetricotlp.NewExportResponse() - partialSuccess := response.PartialSuccess() - partialSuccess.SetErrorMessage("Some data points were not ingested") - partialSuccess.SetRejectedDataPoints(1) - - return response - }) - - // Send two metrics. - md = testdata.GenerateMetrics(2) - assert.NoError(t, exp.ConsumeMetrics(context.Background(), md)) -} - -func TestSendTraceDataServerDownAndUp(t *testing.T) { - // Find the addr, but don't start the server. - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - - // Start an OTel-Arrow exporter and point to the receiver. - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - // Disable queuing to ensure that we execute the request when calling ConsumeTraces - // otherwise we will not see the error. - cfg.QueueSettings.Enabled = false - cfg.ClientConfig = configgrpc.ClientConfig{ - Endpoint: ln.Addr().String(), - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - // Need to wait for every request blocking until either request timeouts or succeed. - // Do not rely on external retry logic here, if that is intended set InitialInterval to 100ms. - WaitForReady: true, - } - cfg.Arrow.MaxStreamLifetime = 100 * time.Second - set := exportertest.NewNopSettings() - exp, err := factory.CreateTracesExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - defer func() { - assert.NoError(t, exp.Shutdown(context.Background())) - }() - - host := componenttest.NewNopHost() - - assert.NoError(t, exp.Start(context.Background(), host)) - - // A trace with 2 spans. - td := testdata.GenerateTraces(2) - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - assert.Error(t, exp.ConsumeTraces(ctx, td)) - assert.EqualValues(t, context.DeadlineExceeded, ctx.Err()) - cancel() - - ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) - assert.Error(t, exp.ConsumeTraces(ctx, td)) - assert.EqualValues(t, context.DeadlineExceeded, ctx.Err()) - cancel() - - startServerAndMakeRequest(t, exp, td, ln) - - ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) - assert.Error(t, exp.ConsumeTraces(ctx, td)) - assert.EqualValues(t, context.DeadlineExceeded, ctx.Err()) - cancel() - - // First call to startServerAndMakeRequest closed the connection. There is a race condition here that the - // port may be reused, if this gets flaky rethink what to do. - ln, err = net.Listen("tcp", ln.Addr().String()) - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - startServerAndMakeRequest(t, exp, td, ln) - - ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) - assert.Error(t, exp.ConsumeTraces(ctx, td)) - assert.EqualValues(t, context.DeadlineExceeded, ctx.Err()) - cancel() -} - -func TestSendTraceDataServerStartWhileRequest(t *testing.T) { - // Find the addr, but don't start the server. - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - - // Start an OTel-Arrow exporter and point to the receiver. - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.ClientConfig = configgrpc.ClientConfig{ - Endpoint: ln.Addr().String(), - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - } - cfg.Arrow.MaxStreamLifetime = 100 * time.Second - set := exportertest.NewNopSettings() - exp, err := factory.CreateTracesExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - defer func() { - assert.NoError(t, exp.Shutdown(context.Background())) - }() - - host := componenttest.NewNopHost() - - assert.NoError(t, exp.Start(context.Background(), host)) - - // A trace with 2 spans. - td := testdata.GenerateTraces(2) - done := make(chan bool, 1) - defer close(done) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - go func() { - assert.NoError(t, exp.ConsumeTraces(ctx, td)) - done <- true - }() - - time.Sleep(2 * time.Second) - rcv, _ := otelArrowTracesReceiverOnGRPCServer(ln, false) - rcv.start() - defer rcv.srv.GracefulStop() - // Wait until one of the conditions below triggers. - select { - case <-ctx.Done(): - t.Fail() - case <-done: - assert.NoError(t, ctx.Err()) - } - cancel() -} - -func TestSendTracesOnResourceExhaustion(t *testing.T) { - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err) - rcv, _ := otelArrowTracesReceiverOnGRPCServer(ln, false) - rcv.setExportError(status.Error(codes.ResourceExhausted, "resource exhausted")) - rcv.start() - defer rcv.srv.GracefulStop() - - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.RetryConfig.InitialInterval = 0 - cfg.ClientConfig = configgrpc.ClientConfig{ - Endpoint: ln.Addr().String(), - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - } - cfg.Arrow.MaxStreamLifetime = 100 * time.Second - set := exportertest.NewNopSettings() - exp, err := factory.CreateTracesExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - - defer func() { - assert.NoError(t, exp.Shutdown(context.Background())) - }() - - host := componenttest.NewNopHost() - assert.NoError(t, exp.Start(context.Background(), host)) - - assert.EqualValues(t, 0, rcv.requestCount.Load()) - - td := ptrace.NewTraces() - assert.NoError(t, exp.ConsumeTraces(context.Background(), td)) - - assert.Never(t, func() bool { - return rcv.requestCount.Load() > 1 - }, 1*time.Second, 5*time.Millisecond, "Should not retry if RetryInfo is not included into status details by the server.") - - rcv.requestCount.Swap(0) - - st := status.New(codes.ResourceExhausted, "resource exhausted") - st, _ = st.WithDetails(&errdetails.RetryInfo{ - RetryDelay: durationpb.New(100 * time.Millisecond), - }) - rcv.setExportError(st.Err()) - - assert.NoError(t, exp.ConsumeTraces(context.Background(), td)) - - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 1 - }, 10*time.Second, 5*time.Millisecond, "Should retry if RetryInfo is included into status details by the server.") -} - -func startServerAndMakeRequest(t *testing.T, exp exporter.Traces, td ptrace.Traces, ln net.Listener) { - rcv, _ := otelArrowTracesReceiverOnGRPCServer(ln, false) - rcv.start() - defer rcv.srv.GracefulStop() - // Ensure that initially there is no data in the receiver. - assert.EqualValues(t, 0, rcv.requestCount.Load()) - - // Clone the request and store as expected. - expectedData := ptrace.NewTraces() - td.CopyTo(expectedData) - - // Resend the request, this should succeed. - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - assert.NoError(t, exp.ConsumeTraces(ctx, td)) - cancel() - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 0 - }, 10*time.Second, 5*time.Millisecond) - - // Verify received span. - assert.EqualValues(t, 2, rcv.totalItems.Load()) - assert.EqualValues(t, expectedData, rcv.getLastRequest()) -} - -func TestSendLogData(t *testing.T) { - // Start an OTel-Arrow receiver. - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - rcv := otelArrowLogsReceiverOnGRPCServer(ln) - // Also closes the connection. - defer rcv.srv.GracefulStop() - - // Start an OTel-Arrow exporter and point to the receiver. - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - // Disable queuing to ensure that we execute the request when calling ConsumeLogs - // otherwise we will not see any errors. - cfg.QueueSettings.Enabled = false - cfg.ClientConfig = configgrpc.ClientConfig{ - Endpoint: ln.Addr().String(), - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - } - cfg.Arrow.MaxStreamLifetime = 100 * time.Second - set := exportertest.NewNopSettings() - set.BuildInfo.Description = "Collector" - set.BuildInfo.Version = "1.2.3test" - exp, err := factory.CreateLogsExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - defer func() { - assert.NoError(t, exp.Shutdown(context.Background())) - }() - - host := componenttest.NewNopHost() - - assert.NoError(t, exp.Start(context.Background(), host)) - - // Ensure that initially there is no data in the receiver. - assert.EqualValues(t, 0, rcv.requestCount.Load()) - - // Send empty request. - ld := plog.NewLogs() - assert.NoError(t, exp.ConsumeLogs(context.Background(), ld)) - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 0 - }, 10*time.Second, 5*time.Millisecond) - - // Ensure it was received empty. - assert.EqualValues(t, 0, rcv.totalItems.Load()) - - // A request with 2 log entries. - ld = testdata.GenerateLogs(2) - - err = exp.ConsumeLogs(context.Background(), ld) - assert.NoError(t, err) - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 1 - }, 10*time.Second, 5*time.Millisecond) - - // Verify received logs. - assert.EqualValues(t, 2, rcv.requestCount.Load()) - assert.EqualValues(t, 2, rcv.totalItems.Load()) - assert.EqualValues(t, ld, rcv.getLastRequest()) - - md := rcv.getMetadata() - require.Equal(t, len(md.Get("User-Agent")), 1) - require.Contains(t, md.Get("User-Agent")[0], "Collector/1.2.3test") - - st := status.New(codes.InvalidArgument, "Invalid argument") - rcv.setExportError(st.Err()) - - // A request with 2 log entries. - ld = testdata.GenerateLogs(2) - - err = exp.ConsumeLogs(context.Background(), ld) - assert.Error(t, err) - - rcv.setExportError(nil) - - // Return partial success - rcv.setExportResponse(func() plogotlp.ExportResponse { - response := plogotlp.NewExportResponse() - partialSuccess := response.PartialSuccess() - partialSuccess.SetErrorMessage("Some log records were not ingested") - partialSuccess.SetRejectedLogRecords(1) - - return response - }) - - // A request with 2 log entries. - ld = testdata.GenerateLogs(2) - - err = exp.ConsumeLogs(context.Background(), ld) - assert.NoError(t, err) -} - -// TestSendArrowTracesNotSupported tests a successful OTel-Arrow export w/ -// and without Arrow, w/ WaitForReady and without. -func TestSendArrowTracesNotSupported(t *testing.T) { - for _, waitForReady := range []bool{true, false} { - for _, available := range []bool{true, false} { - t.Run(fmt.Sprintf("waitForReady=%v available=%v", waitForReady, available), - func(t *testing.T) { testSendArrowTraces(t, waitForReady, available) }) - } - } -} - -func testSendArrowTraces(t *testing.T, clientWaitForReady, streamServiceAvailable bool) { - // Start an OTel-Arrow receiver. - ln, err := net.Listen("tcp", "127.0.0.1:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - - // Start an OTel-Arrow exporter and point to the receiver. - factory := NewFactory() - authID := component.NewID(component.MustNewType("testauth")) - expectedHeader := []string{"arrow-ftw"} - cfg := factory.CreateDefaultConfig().(*Config) - cfg.ClientConfig = configgrpc.ClientConfig{ - Endpoint: ln.Addr().String(), - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - WaitForReady: clientWaitForReady, - Headers: map[string]configopaque.String{ - "header": configopaque.String(expectedHeader[0]), - }, - Auth: &configauth.Authentication{ - AuthenticatorID: authID, - }, - } - // Arrow client is enabled, but the server doesn't support it. - cfg.Arrow = ArrowConfig{ - NumStreams: 1, - MaxStreamLifetime: 100 * time.Second, - } - - set := exportertest.NewNopSettings() - set.TelemetrySettings.Logger = zaptest.NewLogger(t) - exp, err := factory.CreateTracesExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - - type isUserCall struct{} - - host := newHostWithExtensions( - map[component.ID]component.Component{ - authID: newTestAuthExtension(t, func(ctx context.Context) map[string]string { - if ctx.Value(isUserCall{}) == nil { - return nil - } - return map[string]string{ - "callerid": "arrow", - } - }), - }, - ) - assert.NoError(t, exp.Start(context.Background(), host)) - - rcv, _ := otelArrowTracesReceiverOnGRPCServer(ln, false) - - defer func() { - // Shutdown before GracefulStop, because otherwise we - // wait for a full stream lifetime instead of closing - // after requests are served. - assert.NoError(t, exp.Shutdown(context.Background())) - rcv.srv.GracefulStop() - }() - - if streamServiceAvailable { - rcv.startStreamMockArrowTraces(t, okStatusFor) - } - - // Delay the server start, slightly. - go func() { - time.Sleep(100 * time.Millisecond) - rcv.start() - }() - - // Send two trace items. - td := testdata.GenerateTraces(2) - - // Set the context key indicating this is per-request state, - // so the auth extension returns data. - err = exp.ConsumeTraces(context.WithValue(context.Background(), isUserCall{}, true), td) - assert.NoError(t, err) - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 0 - }, 10*time.Second, 5*time.Millisecond) - - // Verify two items, one request received. - assert.EqualValues(t, int32(2), rcv.totalItems.Load()) - assert.EqualValues(t, int32(1), rcv.requestCount.Load()) - assert.EqualValues(t, td, rcv.getLastRequest()) - - // Expect the correct metadata, with or without arrow. - md := rcv.getMetadata() - require.EqualValues(t, []string{"arrow"}, md.Get("callerid")) - require.EqualValues(t, expectedHeader, md.Get("header")) -} - -func okStatusFor(id int64) *arrowpb.BatchStatus { - return &arrowpb.BatchStatus{ - BatchId: id, - StatusCode: arrowpb.StatusCode_OK, - } -} - -func failedStatusFor(id int64) *arrowpb.BatchStatus { - return &arrowpb.BatchStatus{ - BatchId: id, - StatusCode: arrowpb.StatusCode_INVALID_ARGUMENT, - StatusMessage: "test failed", - } -} - -type anyStreamServer interface { - Send(*arrowpb.BatchStatus) error - Recv() (*arrowpb.BatchArrowRecords, error) - grpc.ServerStream -} - -func (r *mockTracesReceiver) startStreamMockArrowTraces(t *testing.T, statusFor func(int64) *arrowpb.BatchStatus) { - ctrl := gomock.NewController(t) - - doer := func(server anyStreamServer) error { - consumer := arrowRecord.NewConsumer() - var hdrs []hpack.HeaderField - hdrsDecoder := hpack.NewDecoder(4096, func(hdr hpack.HeaderField) { - hdrs = append(hdrs, hdr) - }) - for { - records, err := server.Recv() - if status, ok := status.FromError(err); ok && status.Code() == codes.Canceled { - break - } - if err != nil { - // No errors are allowed, except EOF. - require.Equal(t, io.EOF, err) - break - } - - got, err := consumer.TracesFrom(records) - require.NoError(t, err) - - // Reset and parse headers - hdrs = nil - _, err = hdrsDecoder.Write(records.Headers) - require.NoError(t, err) - md, ok := metadata.FromIncomingContext(server.Context()) - require.True(t, ok) - - for _, hf := range hdrs { - md[hf.Name] = append(md[hf.Name], hf.Value) - } - - // Place the metadata into the context, where - // the test framework (independent of Arrow) - // receives it. - ctx := metadata.NewIncomingContext(context.Background(), md) - - for _, traces := range got { - _, err := r.Export(ctx, ptraceotlp.NewExportRequestFromTraces(traces)) - require.NoError(t, err) - } - require.NoError(t, server.Send(statusFor(records.BatchId))) - } - return nil - } - - type singleBinding struct { - arrowpb.UnsafeArrowTracesServiceServer - *arrowpbMock.MockArrowTracesServiceServer - } - svc := arrowpbMock.NewMockArrowTracesServiceServer(ctrl) - - arrowpb.RegisterArrowTracesServiceServer(r.srv, singleBinding{ - MockArrowTracesServiceServer: svc, - }) - svc.EXPECT().ArrowTraces(gomock.Any()).Times(1).DoAndReturn(doer) - -} - -func TestSendArrowFailedTraces(t *testing.T) { - // Start an OTel-Arrow receiver. - ln, err := net.Listen("tcp", "127.0.0.1:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - - // Start an OTel-Arrow exporter and point to the receiver. - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.ClientConfig = configgrpc.ClientConfig{ - Endpoint: ln.Addr().String(), - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - WaitForReady: true, - } - // Arrow client is enabled, but the server doesn't support it. - cfg.Arrow = ArrowConfig{ - NumStreams: 1, - MaxStreamLifetime: 100 * time.Second, - } - cfg.QueueSettings.Enabled = false - - set := exportertest.NewNopSettings() - set.TelemetrySettings.Logger = zaptest.NewLogger(t) - exp, err := factory.CreateTracesExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - - host := componenttest.NewNopHost() - assert.NoError(t, exp.Start(context.Background(), host)) - - rcv, _ := otelArrowTracesReceiverOnGRPCServer(ln, false) - rcv.startStreamMockArrowTraces(t, failedStatusFor) - - defer func() { - assert.NoError(t, exp.Shutdown(context.Background())) - rcv.srv.GracefulStop() - }() - - // Delay the server start, slightly. - go func() { - time.Sleep(100 * time.Millisecond) - rcv.start() - }() - - // Send two trace items. - td := testdata.GenerateTraces(2) - err = exp.ConsumeTraces(context.Background(), td) - assert.Error(t, err) - assert.Contains(t, err.Error(), "test failed") - - // Wait until it is received. - assert.Eventually(t, func() bool { - return rcv.requestCount.Load() > 0 - }, 10*time.Second, 5*time.Millisecond) - - // Verify two items, one request received. - assert.EqualValues(t, int32(2), rcv.totalItems.Load()) - assert.EqualValues(t, int32(1), rcv.requestCount.Load()) - assert.EqualValues(t, td, rcv.getLastRequest()) -} - -func TestUserDialOptions(t *testing.T) { - // Start an OTel-Arrow receiver. - ln, err := net.Listen("tcp", "127.0.0.1:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - - // Start an OTel-Arrow exporter and point to the receiver. - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.ClientConfig = configgrpc.ClientConfig{ - Endpoint: ln.Addr().String(), - TLSSetting: configtls.ClientConfig{ - Insecure: true, - }, - WaitForReady: true, - } - cfg.Arrow.Disabled = true - cfg.QueueSettings.Enabled = false - - const testAgent = "test-user-agent (release=:+1:)" - - // This overrides the default provided in otelArrow.go - cfg.UserDialOptions = []grpc.DialOption{ - grpc.WithUserAgent(testAgent), - } - - set := exportertest.NewNopSettings() - set.TelemetrySettings.Logger = zaptest.NewLogger(t) - exp, err := factory.CreateTracesExporter(context.Background(), set, cfg) - require.NoError(t, err) - require.NotNil(t, exp) - - defer func() { - assert.NoError(t, exp.Shutdown(context.Background())) - }() - - host := componenttest.NewNopHost() - assert.NoError(t, exp.Start(context.Background(), host)) - - td := testdata.GenerateTraces(2) - - rcv, _ := otelArrowTracesReceiverOnGRPCServer(ln, false) - rcv.start() - defer rcv.srv.GracefulStop() - - err = exp.ConsumeTraces(context.Background(), td) - assert.NoError(t, err) - - require.Equal(t, len(rcv.getMetadata().Get("User-Agent")), 1) - require.Contains(t, rcv.getMetadata().Get("User-Agent")[0], testAgent) -} diff --git a/collector/exporter/otelarrowexporter/testdata/config.yaml b/collector/exporter/otelarrowexporter/testdata/config.yaml deleted file mode 100644 index db9e8016..00000000 --- a/collector/exporter/otelarrowexporter/testdata/config.yaml +++ /dev/null @@ -1,33 +0,0 @@ -endpoint: "1.2.3.4:1234" -compression: "none" -tls: - ca_file: /var/lib/mycert.pem -timeout: 10s -sending_queue: - enabled: true - num_consumers: 2 - queue_size: 10 -retry_on_failure: - enabled: true - initial_interval: 10s - randomization_factor: 0.7 - multiplier: 1.3 - max_interval: 60s - max_elapsed_time: 10m -auth: - authenticator: nop -headers: - "can you have a . here?": "F0000000-0000-0000-0000-000000000000" - header1: 234 - another: "somevalue" -keepalive: - time: 20s - timeout: 30s - permit_without_stream: true -balancer_name: "experimental" -arrow: - num_streams: 2 - disabled: false - max_stream_lifetime: 2h - payload_compression: "zstd" - prioritizer: leastloaded8 diff --git a/collector/exporter/otelarrowexporter/testdata/default.yaml b/collector/exporter/otelarrowexporter/testdata/default.yaml deleted file mode 100644 index 458203ea..00000000 --- a/collector/exporter/otelarrowexporter/testdata/default.yaml +++ /dev/null @@ -1 +0,0 @@ -# when nothing is set diff --git a/collector/exporter/otelarrowexporter/testdata/test_cert.pem b/collector/exporter/otelarrowexporter/testdata/test_cert.pem deleted file mode 100644 index b3842e59..00000000 --- a/collector/exporter/otelarrowexporter/testdata/test_cert.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICpDCCAYwCCQC5oaFsqLW3GTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMjEwNzE0MDAxMzU2WhcNMzEwNzEyMDAxMzU2WjAUMRIwEAYD -VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDO -mKaE1qg5VLMwaUnSzufT23rRJFbuy/HDXwsH63yZVSsISQkGjkBYBgrqAMtVnsI/ -l4gXtBWkZtJFs68Sbo9ps3W0PdB5+d12R5NUNA1rkZtx3jtEN33dpGhifug/TIZe -7Zr0G1z6gNoaEezk0Jpg4KsH7QpIeHPRhIZMyWeqddgD/qL4/ukaU4NOORuF3WoT -oo2LpI3jUq66mz2N2Inq0V/OX7BYB4Ur6EtjWh2baiUuw9fq+oLUlgZd6ypnugC/ -+YfgYqvWtRntmEr0Z+O4Kz81P2IpH/0h1RFhWyK6thVGa9cx6aseCp3V2cMXfGfc -z4n3Uvz87v+bZvGbcse/AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAlvNBNoqXUQ -ohR0eozIHGeJ94U7WK5zXf2NSvmRlwHzHXvUq6GKd+8Bv1foMjI6OpSOZmjtRGsc -rWET1WjSyQddRfqYazhWp1IyYu5LfATwPS+RXJAkWixKVfG+Ta2x6u+aT/bSZwEg -NwRerc6pyqv5UG8Z7Pe1kAxbgOwZv5KXAewIgTSbEkmIp1Dg8GhGeWD5pjYNCkJV -Na2KMAUWP3PeQzdSBKmBNpsRUALuSTxb5u7pl+PA7FLInTtDeyZn8xpO1GPBhbJE -trDbmTbj5YexOXEaQtGtZ6fwRw2jnUm8nqtXozxIomnVTBO8vLmZAUgyJ71trRw0 -gE9tH5Ndlug= ------END CERTIFICATE----- diff --git a/collector/exporter/otelarrowexporter/testdata/test_key.pem b/collector/exporter/otelarrowexporter/testdata/test_key.pem deleted file mode 100644 index dedfad3d..00000000 --- a/collector/exporter/otelarrowexporter/testdata/test_key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDOmKaE1qg5VLMw -aUnSzufT23rRJFbuy/HDXwsH63yZVSsISQkGjkBYBgrqAMtVnsI/l4gXtBWkZtJF -s68Sbo9ps3W0PdB5+d12R5NUNA1rkZtx3jtEN33dpGhifug/TIZe7Zr0G1z6gNoa -Eezk0Jpg4KsH7QpIeHPRhIZMyWeqddgD/qL4/ukaU4NOORuF3WoToo2LpI3jUq66 -mz2N2Inq0V/OX7BYB4Ur6EtjWh2baiUuw9fq+oLUlgZd6ypnugC/+YfgYqvWtRnt -mEr0Z+O4Kz81P2IpH/0h1RFhWyK6thVGa9cx6aseCp3V2cMXfGfcz4n3Uvz87v+b -ZvGbcse/AgMBAAECggEADeR39iDVKR3H+u5pl3JwZm+w35V4/w/ZzxB6FmtAcrMm -dKUspTM1onWtkDTDd5t4ZnxTG3zxo5+Cbkt571xd6na16Ivrk/g4aza+8n+Zk200 -LcEK7ThqD1h56H2uMmt78bA6pkWcx/+YKv6flndsmi0hcyP+eAcZirJFsa4teWna -P6rhI9zThc9OcecqGZIlmzJQ4cLbIO86QqkWW6yjKYg6riOb2g+i3e97ZngMCTcV -lni+sksLlXBNKPqh1AkiUFe4pInRBh4LGQ5rNSYswEqlQY0iW0u4Hs3HNou0On+8 -1T8m5wzKQ+23AN+vVRJ/MHssQiB/TPK92jXVgEz6eQKBgQD2GEb7NzDIxsAQZBQo -tt3jYitNcAEqMWeT7wxCMMue4wIrT6Fp6NuG5NMVqLglzx72m6TXg7YzZxPrAnlH -jblWI4sxwVC8BjjYyGud7qMuhUIZmI8aS9HuYW0ODSxkcpVVXd4HDUYKg7PafAkl -cj745E5KGD+qW44KASTTQ1SwRQKBgQDW6WLp/nPVPO5YEK4nzS7b1RRC8ypHiKd6 -LzhA2izgcsmO3F3Y5ZZ5rzeFbjgZiGFTUB/r1mgomI8kZyIGP1AN6o8oY9I89gHY -/DEEagIsFK5jAEoMeN0qbgqasOXpi+uUHCNidWa7OWOL9Rsh7dyVT54xcqMC2Qak -Vpoy5miiMwKBgQDuOHH9nF9M+5fQRhB9mQcRpWXlgBagkVKCkVR8fl+dXoIrCtpl -e1OGMNtki/42G1kNv3zCYm1tNMrDI5HjAf32tFF5yHguipdcwiXqq6aq0bQ6ssNT -4TFGYGkAwR/H3GNST5stmFvEsdjYFlmENiNfKyHd97spXZcReCn9l5/TQQKBgDRG -PpYWG4zBrmPjYskxonU8ZhpG1YDi34Hb3H4B06qgoSBLv9QTPD/K++FLxv+G6c1/ -DtSpqVo+iYrcPy1v1wQbisjTRv8nA5oI9c9SDcc1HJneJyTTfVBlxdSMtM/TBfFX -ys+XKO7fbbRMYVYmamIzJJJ4hOgba/8rRYSeANN7AoGBAMDdrT+ig3aDMratbAvY -lqsfN3AtxoZ+ZVQYyUbzTSZPZ/to9eNuBzhRKcQ3QfG95nrHb7OnWHa7+1kc4p/Q -jMgzJgRpajlES+F3CCMPgJIJg7Ev+yiSCJLP9ZOsC+E96bK265hUcDyCXwb3Wzmg -4L9sc1QsQW80QO/RnaEzGO51 ------END PRIVATE KEY----- diff --git a/collector/otelarrowcol-build.yaml b/collector/otelarrowcol-build.yaml index 421a8a4e..a6e0e231 100644 --- a/collector/otelarrowcol-build.yaml +++ b/collector/otelarrowcol-build.yaml @@ -22,7 +22,7 @@ dist: # Note: This should match the version of the core and contrib # collector components used below (e.g., the debugexporter and # otlphttpexporter versions below). - otelcol_version: 0.102.1 + otelcol_version: 0.105.0 # Project-internal use: Directory path required for the `make # genotelarrowcol`, which the Dockerfile also recognizes. @@ -32,18 +32,20 @@ dist: exporters: # This is the core OpenTelemetry Protocol with Apache Arrow exporter - - gomod: github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter v0.24.0 - - gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.102.1 - - gomod: go.opentelemetry.io/collector/exporter/otlphttpexporter v0.102.1 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter v0.104.0 + + - gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.105.0 + - gomod: go.opentelemetry.io/collector/exporter/otlphttpexporter v0.105.0 - gomod: github.com/open-telemetry/otel-arrow/collector/exporter/fileexporter v0.24.0 receivers: # This is the core OpenTelemetry Protocol with Apache Arrow receiver - - gomod: github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver v0.24.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver v0.104.0 + - gomod: github.com/open-telemetry/otel-arrow/collector/receiver/filereceiver v0.24.0 # Users wanting the OTLP/HTTP Receiver will use the otlp receiver. # Users wanting OTLP/gRPC may use the otelarrowreceiver. - - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.102.1 + - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.105.0 processors: - gomod: github.com/open-telemetry/otel-arrow/collector/processor/concurrentbatchprocessor v0.24.0 @@ -53,6 +55,6 @@ connectors: - gomod: github.com/open-telemetry/otel-arrow/collector/connector/validationconnector v0.24.0 extensions: - - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.102.0 - - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.102.0 - - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.102.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/headerssetterextension v0.104.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/basicauthextension v0.104.0 + - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.104.0 diff --git a/collector/receiver/otelarrowreceiver/README.md b/collector/receiver/otelarrowreceiver/README.md deleted file mode 100644 index 557a2757..00000000 --- a/collector/receiver/otelarrowreceiver/README.md +++ /dev/null @@ -1,199 +0,0 @@ -# OTel-Arrow Receiver - - -| Status | | -| ------------- |-----------| -| Stability | [development]: metrics, traces, logs | -| Distributions | [otelarrow] | -| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Fotelarrow%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Fotelarrow) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Fotelarrow%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Fotelarrow) | - -[development]: https://github.com/open-telemetry/opentelemetry-collector#development -[otelarrow]: - - -Receives telemetry data using the -[OTel-Arrow](https://github.com/open-telemetry/otel-arrow) protocol -via gRPC and standard [OTLP]( -https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md) -protocol via gRPC. - -## Getting Started - -The OTel-Arrow receiver is an extension of the core OpenTelemetry -Collector [OTLP -receiver](https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver/otlpreceiver) -component with additional support for the -[OTel-Arrow](https://github.com/open-telemetry/otel-arrow) protocol. - -OTel-Arrow supports column-oriented data transport using the Apache -Arrow data format. The [OTel-Arrow -exporter](https://github.com/open-telemetry/otel-arrow/blob/main/collector/exporter/otelarrowexporter/README.md) -converts OTLP data into an optimized representation and then sends -batches of data using Apache Arrow to encode the stream. This -component contains logic to reverse the process used in the OTel-Arrow -exporter. - -The use of an OTel-Arrow exporter-receiver pair is recommended when -the network is expensive. Typically, expect to see a 50% reduction in -bandwidth compared with the same data being sent using standard -OTLP/gRPC and gzip compression. - -This component includes all the features and configuration of the core -OTLP receiver, making it possible to upgrade from the core component -simply by replacing "otlp" with "otelarrow" as the component name in -the collector configuration. - -To enable the OTel-Arrow receiver, include it in the list of receivers -for a pipeline. No further configuration is needed. This receiver -listens on the standard OTLP/gRPC port 4317 and serves standard OTLP -over gRPC out of the box. - -```yaml -receivers: - otelarrow: -``` - -## Advanced Configuration - -Users may wish to configure gRPC settings, for example: - -``` -receivers: - otelarrow: - protocols: - grpc: - ... -``` - -- `endpoint` (default = 0.0.0.0:4317 for grpc protocol): - host:port to which the receiver is going to receive data. The valid syntax is - described at https://github.com/grpc/grpc/blob/master/doc/naming.md. - -Several common configuration structures provide additional capabilities automatically: - -- [gRPC settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configgrpc/README.md) -- [TLS and mTLS settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configtls/README.md) - -### Arrow-specific Configuration - -In the `arrow` configuration block, the following settings are available: - -- `memory_limit_mib` (default: 128): limits the amount of concurrent memory used by Arrow data buffers. - -When the limit is reached, the receiver will return RESOURCE_EXHAUSTED -error codes to the receiver, which are [conditionally retryable, see -exporter retry configuration](https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/exporterhelper/README.md). - -- `admission_limit_mib` (default: 64): limits the number of requests that are received by the stream based on request size information available. This should not be confused with `memory_limit_mib` which limits allocations made by the consumer when translating arrow records into pdata objects. i.e. request size is used to control how much traffic we admit, but does not control how much memory is used during request processing. - -- `waiter_limit` (default: 1000): limits the number of requests waiting on admission once `admission_limit_mib` is reached. This is another dimension of memory limiting that ensures waiters are not holding onto a significant amount of memory while waiting to be processed. - -`admission_limit_mib` and `waiter_limit` are arguments supplied to [admission.BoundedQueue](https://github.com/open-telemetry/otel-arrow/tree/main/collector/admission). This custom semaphore is meant to be used within receivers to help limit memory within the collector pipeline. - -### Compression Configuration - -In the `arrow` configuration block, `zstd` sub-section applies to all -compression levels used by exporters: - -- `memory_limit_mib` limits memory dedicated to Zstd decompression, per stream (default 128) -- `max_window_size_mib`: maximum size of the Zstd window in MiB, 0 indicates to determine based on level (default 32) -- `concurrency`: controls background CPU used for decompression, 0 indicates to let `zstd` library decide (default 1) - -### Keepalive configuration - -As a gRPC streaming service, the OTel Arrow receiver is able to limit -stream lifetime through configuration of the underlying http/2 -connection via keepalive settings. - -Keepalive settings are vital to the operation of OTel Arrow, because -longer-lived streams use more memory and streams are fixed to a single -host. Since every stream of data is different, we recommend -experimenting to find a good balance between memory usage, stream -lifetime, and load balance. - -gRPC libraries do not build-in a facility for long-lived RPCs to learn -about impending http/2 connection state changes, including the event -that initiates connection reset. While the receiver knows its own -keepalive settings, a shorter maximum connection lifetime can be -imposed by intermediate http/2 proxies, and therefore the receiver and -exporter are expected to independently configure these limits. - -``` -receivers: - otelarrow: - protocols: - grpc: - keepalive: - server_parameters: - max_connection_age: 1m - max_connection_age_grace: 10m -``` - -In the example configuration above, OTel-Arrow streams will have reset -initiated after 10 minutes. Note that `max_connection_age` is set to -a small value and we recommend tuning `max_connection_age_grace`. - -OTel Arrow exporters are expected to configure their -`max_stream_lifetime` property to a value that is slightly smaller -than the receiver's `max_connection_age_grace` setting, which causes -the exporter to cleanly shut down streams, allowing requests to -complete before the http/2 connection is forcibly closed. While the -exporter will retry data that was in-flight during an unexpected -stream shutdown, instrumentation about the telemety pipeline will show -RPC errors when the exporter's `max_stream_lifetime` is not configured -correctly. - -[See the exporter README for more -guidance](../../exporter/otelarrowexporter/README.md). For the -example where `max_connection_age_grace` is set to 10 minutes, the -exporter's `max_stream_lifetime` should be set to the same number -minus a reasonable timeout to allow in-flight requests to complete. -For example, an exporter with `9m30s` stream lifetime: - -``` -exporters: - otelarrow: - timeout: 30s - arrow: - max_stream_lifetime: 9m30s - endpoint: ... - tls: ... -``` - -### Receiver metrics - -In addition to the the standard -[obsreport](https://pkg.go.dev/go.opentelemetry.io/collector/obsreport) -metrics, this component provides network-level measurement instruments -which we anticipate will become part of `obsreport` in the future. At -the `normal` level of metrics detail: - -- `receiver_recv`: uncompressed bytes received, prior to compression -- `receiver_recv_wire`: compressed bytes received, on the wire. - -Arrow's compression performance can be derived by dividing the average -`receiver_recv` value by the average `receiver_recv_wire` value. - -At the `detailed` metrics detail level, information about the stream -of data being returned from the receiver will be instrumented: - -- `receiver_sent`: uncompressed bytes sent, prior to compression -- `receiver_sent_wire`: compressed bytes sent, on the wire. - -There several OTel-Arrow-consumer related metrics available to help -diagnose internal performance. These are disabled at the basic level -of detail. At the normal level, these metrics are introduced: - -- `arrow_batch_records`: Counter of Arrow-IPC records processed -- `arrow_memory_inuse`: UpDownCounter of memory in use by current streams -- `arrow_schema_resets`: Counter of times the schema was adjusted, by data type. - -``` -service - ... - telemetry: - ... - metrics: - ... - level: detailed -``` diff --git a/collector/receiver/otelarrowreceiver/config.go b/collector/receiver/otelarrowreceiver/config.go deleted file mode 100644 index 6c5b5575..00000000 --- a/collector/receiver/otelarrowreceiver/config.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowreceiver // import "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver" - -import ( - "fmt" - - "github.com/open-telemetry/otel-arrow/collector/compression/zstd" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configgrpc" -) - -// Protocols is the configuration for the supported protocols. -type Protocols struct { - GRPC configgrpc.ServerConfig `mapstructure:"grpc"` - Arrow ArrowConfig `mapstructure:"arrow"` -} - -// ArrowConfig support configuring the Arrow receiver. -type ArrowConfig struct { - // MemoryLimitMiB is the size of a shared memory region used - // by all Arrow streams, in MiB. When too much load is - // passing through, they will see ResourceExhausted errors. - MemoryLimitMiB uint64 `mapstructure:"memory_limit_mib"` - - // AdmissionLimitMiB limits the number of requests that are received by the stream based on - // request size information available. Request size is used to control how much traffic we admit - // for processing, but does not control how much memory is used during request processing. - AdmissionLimitMiB uint64 `mapstructure:"admission_limit_mib"` - - // WaiterLimit is the limit on the number of waiters waiting to be processed and consumed. - // This is a dimension of memory limiting to ensure waiters are not consuming an - // unexpectedly large amount of memory in the arrow receiver. - WaiterLimit int64 `mapstructure:"waiter_limit"` - - // Zstd settings apply to OTel-Arrow use of gRPC specifically. - Zstd zstd.DecoderConfig `mapstructure:"zstd"` -} - -// Config defines configuration for OTel Arrow receiver. -type Config struct { - // Protocols is the configuration for gRPC and Arrow. - Protocols `mapstructure:"protocols"` -} - -var _ component.Config = (*Config)(nil) -var _ component.ConfigValidator = (*ArrowConfig)(nil) - -func (cfg *ArrowConfig) Validate() error { - if err := cfg.Zstd.Validate(); err != nil { - return fmt.Errorf("zstd decoder: invalid configuration: %w", err) - } - return nil -} diff --git a/collector/receiver/otelarrowreceiver/config_test.go b/collector/receiver/otelarrowreceiver/config_test.go deleted file mode 100644 index fc25f24a..00000000 --- a/collector/receiver/otelarrowreceiver/config_test.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowreceiver - -import ( - "path/filepath" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/config/confignet" - "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/confmap/confmaptest" -) - -func TestUnmarshalDefaultConfig(t *testing.T) { - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "default.yaml")) - require.NoError(t, err) - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.NoError(t, cm.Unmarshal(cfg)) - defaultCfg := factory.CreateDefaultConfig().(*Config) - assert.Equal(t, defaultCfg, cfg) -} - -func TestUnmarshalConfigOnlyGRPC(t *testing.T) { - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "only_grpc.yaml")) - require.NoError(t, err) - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.NoError(t, cm.Unmarshal(cfg)) - - defaultOnlyGRPC := factory.CreateDefaultConfig().(*Config) - assert.Equal(t, defaultOnlyGRPC, cfg) -} - -func TestUnmarshalConfig(t *testing.T) { - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) - require.NoError(t, err) - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.NoError(t, cm.Unmarshal(cfg)) - assert.Equal(t, - &Config{ - Protocols: Protocols{ - GRPC: configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: "0.0.0.0:4317", - Transport: confignet.TransportTypeTCP, - }, - TLSSetting: &configtls.ServerConfig{ - Config: configtls.Config{ - CertFile: "test.crt", - KeyFile: "test.key", - }, - }, - MaxRecvMsgSizeMiB: 32, - MaxConcurrentStreams: 16, - ReadBufferSize: 1024, - WriteBufferSize: 1024, - Keepalive: &configgrpc.KeepaliveServerConfig{ - ServerParameters: &configgrpc.KeepaliveServerParameters{ - MaxConnectionIdle: 11 * time.Second, - MaxConnectionAge: 12 * time.Second, - MaxConnectionAgeGrace: 13 * time.Second, - Time: 30 * time.Second, - Timeout: 5 * time.Second, - }, - EnforcementPolicy: &configgrpc.KeepaliveEnforcementPolicy{ - MinTime: 10 * time.Second, - PermitWithoutStream: true, - }, - }, - }, - Arrow: ArrowConfig{ - MemoryLimitMiB: 123, - AdmissionLimitMiB: 80, - WaiterLimit: 100, - }, - }, - }, cfg) - -} - -func TestUnmarshalConfigUnix(t *testing.T) { - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "uds.yaml")) - require.NoError(t, err) - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.NoError(t, cm.Unmarshal(cfg)) - assert.Equal(t, - &Config{ - Protocols: Protocols{ - GRPC: configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: "/tmp/grpc_otlp.sock", - Transport: confignet.TransportTypeUnix, - }, - ReadBufferSize: 512 * 1024, - }, - Arrow: ArrowConfig{ - MemoryLimitMiB: defaultMemoryLimitMiB, - AdmissionLimitMiB: defaultAdmissionLimitMiB, - WaiterLimit: defaultWaiterLimit, - }, - }, - }, cfg) -} - -func TestUnmarshalConfigTypoDefaultProtocol(t *testing.T) { - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "typo_default_proto_config.yaml")) - require.NoError(t, err) - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.EqualError(t, cm.Unmarshal(cfg), "1 error(s) decoding:\n\n* 'protocols' has invalid keys: htttp") -} - -func TestUnmarshalConfigInvalidProtocol(t *testing.T) { - cm, err := confmaptest.LoadConf(filepath.Join("testdata", "bad_proto_config.yaml")) - require.NoError(t, err) - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.EqualError(t, cm.Unmarshal(cfg), "1 error(s) decoding:\n\n* 'protocols' has invalid keys: thrift") -} - -func TestUnmarshalConfigNoProtocols(t *testing.T) { - cfg := Config{} - // This now produces an error due to breaking change. - // https://github.com/open-telemetry/opentelemetry-collector/pull/9385 - assert.ErrorContains(t, component.ValidateConfig(cfg), "invalid transport type") -} diff --git a/collector/receiver/otelarrowreceiver/doc.go b/collector/receiver/otelarrowreceiver/doc.go deleted file mode 100644 index 188a74b8..00000000 --- a/collector/receiver/otelarrowreceiver/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -//go:generate mdatagen metadata.yaml - -package otelarrowreceiver // import "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver" diff --git a/collector/receiver/otelarrowreceiver/factory.go b/collector/receiver/otelarrowreceiver/factory.go deleted file mode 100644 index 96ec2564..00000000 --- a/collector/receiver/otelarrowreceiver/factory.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowreceiver // import "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver" - -import ( - "context" - - "github.com/open-telemetry/otel-arrow/collector/sharedcomponent" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/config/confignet" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/receiver" - - "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/metadata" -) - -const ( - defaultGRPCEndpoint = "0.0.0.0:4317" - - defaultMemoryLimitMiB = 128 - defaultAdmissionLimitMiB = defaultMemoryLimitMiB / 2 - defaultWaiterLimit = 1000 -) - -// NewFactory creates a new OTel-Arrow receiver factory. -func NewFactory() receiver.Factory { - return receiver.NewFactory( - metadata.Type, - createDefaultConfig, - receiver.WithTraces(createTraces, metadata.TracesStability), - receiver.WithMetrics(createMetrics, metadata.MetricsStability), - receiver.WithLogs(createLog, metadata.LogsStability)) -} - -// createDefaultConfig creates the default configuration for receiver. -func createDefaultConfig() component.Config { - return &Config{ - Protocols: Protocols{ - GRPC: configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: defaultGRPCEndpoint, - Transport: confignet.TransportTypeTCP, - }, - // We almost write 0 bytes, so no need to tune WriteBufferSize. - ReadBufferSize: 512 * 1024, - }, - Arrow: ArrowConfig{ - MemoryLimitMiB: defaultMemoryLimitMiB, - AdmissionLimitMiB: defaultAdmissionLimitMiB, - WaiterLimit: defaultWaiterLimit, - }, - }, - } -} - -// createTraces creates a trace receiver based on provided config. -func createTraces( - _ context.Context, - set receiver.Settings, - cfg component.Config, - nextConsumer consumer.Traces, -) (receiver.Traces, error) { - oCfg := cfg.(*Config) - r, err := receivers.GetOrAdd(oCfg, func() (*otelArrowReceiver, error) { - return newOTelArrowReceiver(oCfg, set) - }) - if err != nil { - return nil, err - } - - r.Unwrap().registerTraceConsumer(nextConsumer) - return r, nil -} - -// createMetrics creates a metrics receiver based on provided config. -func createMetrics( - _ context.Context, - set receiver.Settings, - cfg component.Config, - consumer consumer.Metrics, -) (receiver.Metrics, error) { - oCfg := cfg.(*Config) - r, err := receivers.GetOrAdd(oCfg, func() (*otelArrowReceiver, error) { - return newOTelArrowReceiver(oCfg, set) - }) - if err != nil { - return nil, err - } - - r.Unwrap().registerMetricsConsumer(consumer) - return r, nil -} - -// createLog creates a log receiver based on provided config. -func createLog( - _ context.Context, - set receiver.Settings, - cfg component.Config, - consumer consumer.Logs, -) (receiver.Logs, error) { - oCfg := cfg.(*Config) - r, err := receivers.GetOrAdd(oCfg, func() (*otelArrowReceiver, error) { - return newOTelArrowReceiver(oCfg, set) - }) - if err != nil { - return nil, err - } - - r.Unwrap().registerLogsConsumer(consumer) - return r, nil -} - -// This is the map of already created OTel-Arrow receivers for particular configurations. -// We maintain this map because the Factory is asked trace and metric receivers separately -// when it gets CreateTracesReceiver() and CreateMetricsReceiver() but they must not -// create separate objects, they must use one otelArrowReceiver object per configuration. -// When the receiver is shutdown it should be removed from this map so the same configuration -// can be recreated successfully. -var receivers = sharedcomponent.NewSharedComponents[*Config, *otelArrowReceiver]() diff --git a/collector/receiver/otelarrowreceiver/factory_test.go b/collector/receiver/otelarrowreceiver/factory_test.go deleted file mode 100644 index 77724b5a..00000000 --- a/collector/receiver/otelarrowreceiver/factory_test.go +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowreceiver - -import ( - "context" - "testing" - - "github.com/open-telemetry/otel-arrow/collector/testutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/config/confignet" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/consumer/consumertest" - "go.opentelemetry.io/collector/receiver/receivertest" -) - -func TestCreateDefaultConfig(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig() - assert.NotNil(t, cfg, "failed to create default config") - assert.NoError(t, componenttest.CheckConfigStruct(cfg)) -} - -func TestCreateReceiver(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.GRPC.NetAddr.Endpoint = testutil.GetAvailableLocalAddress(t) - - creationSet := receivertest.NewNopSettings() - tReceiver, err := factory.CreateTracesReceiver(context.Background(), creationSet, cfg, consumertest.NewNop()) - assert.NotNil(t, tReceiver) - assert.NoError(t, err) - - mReceiver, err := factory.CreateMetricsReceiver(context.Background(), creationSet, cfg, consumertest.NewNop()) - assert.NotNil(t, mReceiver) - assert.NoError(t, err) -} - -func TestCreateTracesReceiver(t *testing.T) { - factory := NewFactory() - defaultGRPCSettings := configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: testutil.GetAvailableLocalAddress(t), - Transport: confignet.TransportTypeTCP, - }, - } - - tests := []struct { - name string - cfg *Config - wantErr bool - }{ - { - name: "default", - cfg: &Config{ - Protocols: Protocols{ - GRPC: defaultGRPCSettings, - }, - }, - }, - { - name: "invalid_grpc_port", - cfg: &Config{ - Protocols: Protocols{ - GRPC: configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: "localhost:112233", - Transport: confignet.TransportTypeTCP, - }, - }, - }, - }, - wantErr: true, - }, - } - ctx := context.Background() - creationSet := receivertest.NewNopSettings() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - sink := new(consumertest.TracesSink) - tr, err := factory.CreateTracesReceiver(ctx, creationSet, tt.cfg, sink) - assert.NoError(t, err) - require.NotNil(t, tr) - if tt.wantErr { - assert.Error(t, tr.Start(context.Background(), componenttest.NewNopHost())) - assert.NoError(t, tr.Shutdown(context.Background())) - } else { - assert.NoError(t, tr.Start(context.Background(), componenttest.NewNopHost())) - assert.NoError(t, tr.Shutdown(context.Background())) - } - }) - } -} - -func TestCreateMetricReceiver(t *testing.T) { - factory := NewFactory() - defaultGRPCSettings := configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: testutil.GetAvailableLocalAddress(t), - Transport: confignet.TransportTypeTCP, - }, - } - - tests := []struct { - name string - cfg *Config - wantErr bool - }{ - { - name: "default", - cfg: &Config{ - Protocols: Protocols{ - GRPC: defaultGRPCSettings, - }, - }, - }, - { - name: "invalid_grpc_address", - cfg: &Config{ - Protocols: Protocols{ - GRPC: configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: "327.0.0.1:1122", - Transport: confignet.TransportTypeTCP, - }, - }, - }, - }, - wantErr: true, - }, - } - ctx := context.Background() - creationSet := receivertest.NewNopSettings() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - sink := new(consumertest.MetricsSink) - mr, err := factory.CreateMetricsReceiver(ctx, creationSet, tt.cfg, sink) - assert.NoError(t, err) - require.NotNil(t, mr) - if tt.wantErr { - assert.Error(t, mr.Start(context.Background(), componenttest.NewNopHost())) - } else { - require.NoError(t, mr.Start(context.Background(), componenttest.NewNopHost())) - assert.NoError(t, mr.Shutdown(context.Background())) - } - }) - } -} - -func TestCreateLogReceiver(t *testing.T) { - factory := NewFactory() - defaultGRPCSettings := configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: testutil.GetAvailableLocalAddress(t), - Transport: confignet.TransportTypeTCP, - }, - } - - tests := []struct { - name string - cfg *Config - wantStartErr bool - wantErr bool - sink consumer.Logs - }{ - { - name: "default", - cfg: &Config{ - Protocols: Protocols{ - GRPC: defaultGRPCSettings, - }, - }, - sink: new(consumertest.LogsSink), - }, - { - name: "invalid_grpc_address", - cfg: &Config{ - Protocols: Protocols{ - GRPC: configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: "327.0.0.1:1122", - Transport: confignet.TransportTypeTCP, - }, - }, - }, - }, - wantStartErr: true, - sink: new(consumertest.LogsSink), - }, - } - ctx := context.Background() - creationSet := receivertest.NewNopSettings() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mr, err := factory.CreateLogsReceiver(ctx, creationSet, tt.cfg, tt.sink) - if tt.wantErr { - assert.Error(t, err) - return - } - assert.NoError(t, err) - require.NotNil(t, mr) - - if tt.wantStartErr { - assert.Error(t, mr.Start(context.Background(), componenttest.NewNopHost())) - assert.NoError(t, mr.Shutdown(context.Background())) - } else { - require.NoError(t, mr.Start(context.Background(), componenttest.NewNopHost())) - assert.NoError(t, mr.Shutdown(context.Background())) - } - }) - } -} diff --git a/collector/receiver/otelarrowreceiver/generated_component_test.go b/collector/receiver/otelarrowreceiver/generated_component_test.go deleted file mode 100644 index f5973adf..00000000 --- a/collector/receiver/otelarrowreceiver/generated_component_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package otelarrowreceiver - -import ( - "context" - "testing" - - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/confmap/confmaptest" - "go.opentelemetry.io/collector/consumer/consumertest" - "go.opentelemetry.io/collector/receiver" - "go.opentelemetry.io/collector/receiver/receivertest" -) - -func TestComponentFactoryType(t *testing.T) { - require.Equal(t, "otelarrow", NewFactory().Type().String()) -} - -func TestComponentConfigStruct(t *testing.T) { - require.NoError(t, componenttest.CheckConfigStruct(NewFactory().CreateDefaultConfig())) -} - -func TestComponentLifecycle(t *testing.T) { - factory := NewFactory() - - tests := []struct { - name string - createFn func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) - }{ - - { - name: "logs", - createFn: func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) { - return factory.CreateLogsReceiver(ctx, set, cfg, consumertest.NewNop()) - }, - }, - - { - name: "metrics", - createFn: func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) { - return factory.CreateMetricsReceiver(ctx, set, cfg, consumertest.NewNop()) - }, - }, - - { - name: "traces", - createFn: func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) { - return factory.CreateTracesReceiver(ctx, set, cfg, consumertest.NewNop()) - }, - }, - } - - cm, err := confmaptest.LoadConf("metadata.yaml") - require.NoError(t, err) - cfg := factory.CreateDefaultConfig() - sub, err := cm.Sub("tests::config") - require.NoError(t, err) - require.NoError(t, sub.Unmarshal(&cfg)) - - for _, test := range tests { - t.Run(test.name+"-shutdown", func(t *testing.T) { - c, err := test.createFn(context.Background(), receivertest.NewNopSettings(), cfg) - require.NoError(t, err) - err = c.Shutdown(context.Background()) - require.NoError(t, err) - }) - t.Run(test.name+"-lifecycle", func(t *testing.T) { - firstRcvr, err := test.createFn(context.Background(), receivertest.NewNopSettings(), cfg) - require.NoError(t, err) - host := componenttest.NewNopHost() - require.NoError(t, err) - require.NoError(t, firstRcvr.Start(context.Background(), host)) - require.NoError(t, firstRcvr.Shutdown(context.Background())) - secondRcvr, err := test.createFn(context.Background(), receivertest.NewNopSettings(), cfg) - require.NoError(t, err) - require.NoError(t, secondRcvr.Start(context.Background(), host)) - require.NoError(t, secondRcvr.Shutdown(context.Background())) - }) - } -} diff --git a/collector/receiver/otelarrowreceiver/generated_package_test.go b/collector/receiver/otelarrowreceiver/generated_package_test.go deleted file mode 100644 index e796d486..00000000 --- a/collector/receiver/otelarrowreceiver/generated_package_test.go +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package otelarrowreceiver - -import ( - "go.uber.org/goleak" - "testing" -) - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} diff --git a/collector/receiver/otelarrowreceiver/go.mod b/collector/receiver/otelarrowreceiver/go.mod deleted file mode 100644 index b7c9a0ec..00000000 --- a/collector/receiver/otelarrowreceiver/go.mod +++ /dev/null @@ -1,100 +0,0 @@ -module github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver - -go 1.21.0 - -toolchain go1.22.2 - -require ( - github.com/open-telemetry/otel-arrow v0.24.0 - github.com/open-telemetry/otel-arrow/collector v0.24.0 - github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/confignet v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configtelemetry v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e - go.opentelemetry.io/otel v1.27.0 - go.opentelemetry.io/otel/metric v1.27.0 - go.opentelemetry.io/otel/trace v1.27.0 - go.uber.org/goleak v1.3.0 - go.uber.org/mock v0.4.0 - go.uber.org/multierr v1.11.0 - go.uber.org/zap v1.27.0 - golang.org/x/net v0.25.0 - google.golang.org/grpc v1.64.0 - google.golang.org/protobuf v1.34.2 -) - -require ( - github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect - github.com/apache/arrow/go/v16 v16.1.0 // indirect - github.com/axiomhq/hyperloglog v0.0.0-20230201085229-3ddf4bad03dc // indirect - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/go-logr/logr v1.4.1 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/flatbuffers v24.3.25+incompatible // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.8 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/knadh/koanf/maps v0.1.1 // indirect - github.com/knadh/koanf/providers/confmap v0.1.0 // indirect - github.com/knadh/koanf/v2 v2.1.1 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mostynb/go-grpc-compression v1.2.3 // indirect - github.com/pierrec/lz4/v4 v4.1.21 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.54.0 // indirect - github.com/prometheus/procfs v0.15.0 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect - github.com/x448/float16 v0.8.4 // indirect - github.com/zeebo/xxh3 v1.0.2 // indirect - go.opentelemetry.io/collector/config/configcompression v1.9.0 // indirect - go.opentelemetry.io/collector/config/configopaque v1.9.0 // indirect - go.opentelemetry.io/collector/config/internal v0.102.1 // indirect - go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e // indirect - go.opentelemetry.io/collector/extension v0.102.1 // indirect - go.opentelemetry.io/collector/featuregate v1.9.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.49.0 // indirect - go.opentelemetry.io/otel/sdk v1.27.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) - -replace github.com/open-telemetry/otel-arrow/collector => ../.. - -replace github.com/open-telemetry/otel-arrow => ../../.. diff --git a/collector/receiver/otelarrowreceiver/go.sum b/collector/receiver/otelarrowreceiver/go.sum deleted file mode 100644 index 3aa37376..00000000 --- a/collector/receiver/otelarrowreceiver/go.sum +++ /dev/null @@ -1,257 +0,0 @@ -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/apache/arrow/go/v16 v16.1.0 h1:dwgfOya6s03CzH9JrjCBx6bkVb4yPD4ma3haj9p7FXI= -github.com/apache/arrow/go/v16 v16.1.0/go.mod h1:9wnc9mn6vEDTRIm4+27pEjQpRKuTvBaessPoEXQzxWA= -github.com/axiomhq/hyperloglog v0.0.0-20230201085229-3ddf4bad03dc h1:Keo7wQ7UODUaHcEi7ltENhbAK2VgZjfat6mLy03tQzo= -github.com/axiomhq/hyperloglog v0.0.0-20230201085229-3ddf4bad03dc/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/brianvoe/gofakeit/v6 v6.17.0 h1:obbQTJeHfktJtiZzq0Q1bEpsNUs+yHrYlPVWt7BtmJ4= -github.com/brianvoe/gofakeit/v6 v6.17.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8= -github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= -github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= -github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= -github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= -github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= -github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= -github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= -github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= -github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mostynb/go-grpc-compression v1.2.3 h1:42/BKWMy0KEJGSdWvzqIyOZ95YcR9mLPqKctH7Uo//I= -github.com/mostynb/go-grpc-compression v1.2.3/go.mod h1:AghIxF3P57umzqM9yz795+y1Vjs47Km/Y2FE6ouQ7Lg= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= -github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= -github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= -github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c h1:UmlCWoLNgxxN906BHOXH06/TMeumOrDqdTXeRkYD6d4= -go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:gKjweCX6ve4F7X4RGV3kDN24Bg2eyV6MTCncnaPfRPA= -go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c h1:F17okJGeAtqIZZv/7mZvo6gunwPqdlt40znR0Vo1c1Q= -go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:AM5c/Ohhxj2j/vfCZrwKUD7PrcMpuCbo68rSBibV9U4= -go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c h1:dFcfo09PibmEsdBA2sMLbs+IyBWoPg7NwwEG6eWQRfY= -go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:EJ/PoRVuG1eowA3KQbnW8VonGHGz8jznCXpWG5U8+2A= -go.opentelemetry.io/collector/config/configcompression v1.9.0 h1:B2q6XMO6xiF2s+14XjqAQHGY5UefR+PtkZ0WAlmSqpU= -go.opentelemetry.io/collector/config/configcompression v1.9.0/go.mod h1:6+m0GKCv7JKzaumn7u80A2dLNCuYf5wdR87HWreoBO0= -go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c h1:ZleEsjYf+kxFV5aF8AWHZ4qPFIw/8EQyM2GTnm1ewHo= -go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:Qkn38t0e9y68UlXAWp+S3gsenh09LB9Ct5bJ56inDGQ= -go.opentelemetry.io/collector/config/confignet v0.102.2-0.20240611143128-7dfb57b9ad1c h1:k8bp8JS8b36o3+Pl35cYiSo6pIYV/CW8+etqvRSuoe4= -go.opentelemetry.io/collector/config/confignet v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= -go.opentelemetry.io/collector/config/configopaque v1.9.0 h1:jocenLdK/rVG9UoGlnpiBxXLXgH5NhIXCrVSTyKVYuA= -go.opentelemetry.io/collector/config/configopaque v1.9.0/go.mod h1:8v1yaH4iYjcigbbyEaP/tzVXeFm4AaAsKBF9SBeqaG4= -go.opentelemetry.io/collector/config/configtelemetry v0.102.2-0.20240611143128-7dfb57b9ad1c h1:biIHEgJgIFabkzjRrxyiGs3ZyoJ8jPiJyU8dorKaPWg= -go.opentelemetry.io/collector/config/configtelemetry v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= -go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c h1:Foets1z7XMsh5KwEvGqFhEHem6Kx3xWjfUVUfLk6bF8= -go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:0/mMXy474cvCd4p4VSiZMTaHD/9LwdGbCqXvBPHkDSg= -go.opentelemetry.io/collector/config/internal v0.102.1 h1:HFsFD3xpHUuNHb8/UTz5crJw1cMHzsJQf/86sgD44hw= -go.opentelemetry.io/collector/config/internal v0.102.1/go.mod h1:Vig3dfeJJnuRe1kBNpszBzPoj5eYnR51wXbeq36Zfpg= -go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c h1:LOhGPowRmdpv7HU6HAFkdRvys41RWaijhGmWa7YBOsg= -go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:KgpS7UxH5rkd69CzAzlY2I1heH8Z7eNCZlHmwQBMxNg= -go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c h1:L/FPXl2OoOKniPw1hYzCOk6eljlcwCC681y4plDDE08= -go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:4EV8/Rh+KD6z75EjDDWthN50aFeeRqxsC589EpakV5E= -go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e h1:Z5SmlS/YR1O5FGQbXyv0oqNERCDDCMQVUWIIP+0NqM0= -go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:JUUGttX6dnz9+LDR+RzH2Imrr04SKwfpdtq42yknbnQ= -go.opentelemetry.io/collector/extension v0.102.1 h1:gAvE3w15q+Vv0Tj100jzcDpeMTyc8dAiemHRtJbspLg= -go.opentelemetry.io/collector/extension v0.102.1/go.mod h1:XBxUOXjZpwYLZYOK5u3GWlbBTOKmzStY5eU1R/aXkIo= -go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c h1:+A3fAo4yg/eDswLz67kny4AlMfFWVY3L44IFbCyxZIk= -go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:m7aGTw7yl2Qe5kprqkJeky2wwVKeLYugu6eiS5XcXpY= -go.opentelemetry.io/collector/featuregate v1.9.0 h1:mC4/HnR5cx/kkG1RKOQAvHxxg5Ktmd9gpFdttPEXQtA= -go.opentelemetry.io/collector/featuregate v1.9.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= -go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c h1:f8L2r0f684bJAAZDoTvEWccx34C3kQsePNwy8KzTPqM= -go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE= -go.opentelemetry.io/collector/pdata/testdata v0.102.1 h1:S3idZaJxy8M7mCC4PG4EegmtiSaOuh6wXWatKIui8xU= -go.opentelemetry.io/collector/pdata/testdata v0.102.1/go.mod h1:JEoSJTMgeTKyGxoMRy48RMYyhkA5vCCq/abJq9B6vXs= -go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e h1:Y1zH80bbNuJWd63T5PjpFs4+NrAl8LvMBxqPCf6XBrM= -go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:A/oJHfYc+1auPv6gbp0B/kR8DLtFIDCB/Z/3nDlHVQs= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= -go.opentelemetry.io/otel/exporters/prometheus v0.49.0 h1:Er5I1g/YhfYv9Affk9nJLfH/+qCCVVg1f2R9AbJfqDQ= -go.opentelemetry.io/otel/exporters/prometheus v0.49.0/go.mod h1:KfQ1wpjf3zsHjzP149P4LyAwWRupc6c7t1ZJ9eXpKQM= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= -go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= -go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= -gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 h1:Q2RxlXqh1cgzzUgV261vBO2jI5R/3DD1J2pM0nI4NhU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/collector/receiver/otelarrowreceiver/internal/arrow/Makefile b/collector/receiver/otelarrowreceiver/internal/arrow/Makefile deleted file mode 100644 index 702c361a..00000000 --- a/collector/receiver/otelarrowreceiver/internal/arrow/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# Generate the mock files -.PHONY: mockgen -mockgen: - go install go.uber.org/mock/mockgen@latest - mkdir -p ./mock - mockgen -package mock go.opentelemetry.io/collector/extension/auth Server > mock/auth.go - mockgen -package mock go.opentelemetry.io/collector/consumer Traces,Metrics,Logs > mock/consumer.go diff --git a/collector/receiver/otelarrowreceiver/internal/arrow/arrow.go b/collector/receiver/otelarrowreceiver/internal/arrow/arrow.go deleted file mode 100644 index 202fddd9..00000000 --- a/collector/receiver/otelarrowreceiver/internal/arrow/arrow.go +++ /dev/null @@ -1,899 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package arrow // import "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/arrow" - -import ( - "context" - "errors" - "fmt" - "io" - "net" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - - arrowpb "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1" - "github.com/open-telemetry/otel-arrow/collector/netstats" - arrowRecord "github.com/open-telemetry/otel-arrow/pkg/otel/arrow_record" - "go.opentelemetry.io/collector/client" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/consumer/consumererror" - "go.opentelemetry.io/collector/extension/auth" - "go.opentelemetry.io/collector/pdata/plog" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.opentelemetry.io/collector/pdata/ptrace" - "go.opentelemetry.io/collector/receiver" - "go.opentelemetry.io/collector/receiver/receiverhelper" - "go.opentelemetry.io/otel" - otelcodes "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/propagation" - "go.opentelemetry.io/otel/trace" - "go.uber.org/multierr" - "go.uber.org/zap" - "golang.org/x/net/http2/hpack" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" - - "github.com/open-telemetry/otel-arrow/collector/admission" -) - -const ( - streamFormat = "arrow" - hpackMaxDynamicSize = 4096 - scopeName = "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver" -) - -var ( - ErrNoMetricsConsumer = fmt.Errorf("no metrics consumer") - ErrNoLogsConsumer = fmt.Errorf("no logs consumer") - ErrNoTracesConsumer = fmt.Errorf("no traces consumer") - ErrUnrecognizedPayload = consumererror.NewPermanent(fmt.Errorf("unrecognized OTel-Arrow payload")) -) - -type Consumers interface { - Traces() consumer.Traces - Metrics() consumer.Metrics - Logs() consumer.Logs -} - -type Receiver struct { - Consumers - - arrowpb.UnsafeArrowTracesServiceServer - arrowpb.UnsafeArrowLogsServiceServer - arrowpb.UnsafeArrowMetricsServiceServer - - telemetry component.TelemetrySettings - tracer trace.Tracer - obsrecv *receiverhelper.ObsReport - gsettings configgrpc.ServerConfig - authServer auth.Server - newConsumer func() arrowRecord.ConsumerAPI - netReporter netstats.Interface - recvInFlightBytes metric.Int64UpDownCounter - recvInFlightItems metric.Int64UpDownCounter - recvInFlightRequests metric.Int64UpDownCounter - boundedQueue *admission.BoundedQueue -} - -// receiverStream holds the inFlightWG for a single stream. -type receiverStream struct { - *Receiver - inFlightWG sync.WaitGroup -} - -// New creates a new Receiver reference. -func New( - cs Consumers, - set receiver.Settings, - obsrecv *receiverhelper.ObsReport, - gsettings configgrpc.ServerConfig, - authServer auth.Server, - newConsumer func() arrowRecord.ConsumerAPI, - bq *admission.BoundedQueue, - netReporter netstats.Interface, -) (*Receiver, error) { - tracer := set.TelemetrySettings.TracerProvider.Tracer("otel-arrow-receiver") - var errors, err error - recv := &Receiver{ - Consumers: cs, - obsrecv: obsrecv, - telemetry: set.TelemetrySettings, - tracer: tracer, - authServer: authServer, - newConsumer: newConsumer, - gsettings: gsettings, - netReporter: netReporter, - boundedQueue: bq, - } - - meter := recv.telemetry.MeterProvider.Meter(scopeName) - recv.recvInFlightBytes, err = meter.Int64UpDownCounter( - "otel_arrow_receiver_in_flight_bytes", - metric.WithDescription("Number of bytes in flight"), - metric.WithUnit("By"), - ) - errors = multierr.Append(errors, err) - - recv.recvInFlightItems, err = meter.Int64UpDownCounter( - "otel_arrow_receiver_in_flight_items", - metric.WithDescription("Number of items in flight"), - ) - errors = multierr.Append(errors, err) - - recv.recvInFlightRequests, err = meter.Int64UpDownCounter( - "otel_arrow_receiver_in_flight_requests", - metric.WithDescription("Number of requests in flight"), - ) - errors = multierr.Append(errors, err) - - if errors != nil { - return nil, errors - } - - return recv, nil -} - -// headerReceiver contains the state necessary to decode per-request metadata -// from an arrow stream. -type headerReceiver struct { - // decoder maintains state across the stream. - decoder *hpack.Decoder - - // includeMetadata as configured by gRPC settings. - includeMetadata bool - - // hasAuthServer indicates that headers must be produced - // independent of includeMetadata. - hasAuthServer bool - - // client connection info from the stream context, (optionally - // if includeMetadata) to be extended with per-request metadata. - connInfo client.Info - - // streamHdrs was translated from the incoming context, will be - // merged with per-request metadata. Note that the contents of - // this map are equivalent to connInfo.Metadata, however that - // library does not let us iterate over the map so we recalculate - // this from the gRPC incoming stream context. - streamHdrs map[string][]string - - // tmpHdrs is used by the decoder's emit function during Write. - tmpHdrs map[string][]string -} - -func newHeaderReceiver(streamCtx context.Context, as auth.Server, includeMetadata bool) *headerReceiver { - hr := &headerReceiver{ - includeMetadata: includeMetadata, - hasAuthServer: as != nil, - connInfo: client.FromContext(streamCtx), - } - - // Note that we capture the incoming context if there is an - // Auth plugin configured or includeMetadata is set. - if hr.includeMetadata || hr.hasAuthServer { - if smd, ok := metadata.FromIncomingContext(streamCtx); ok { - hr.streamHdrs = smd - } - } - - // Note the hpack decoder supports additional protections, - // such as SetMaxStringLength(), but as we already have limits - // on stream request size, this seems unnecessary. - hr.decoder = hpack.NewDecoder(hpackMaxDynamicSize, hr.tmpHdrsAppend) - - return hr -} - -// combineHeaders calculates per-request Metadata by combining the stream's -// client.Info with additional key:values associated with the arrow batch. -func (h *headerReceiver) combineHeaders(ctx context.Context, hdrsBytes []byte) (context.Context, map[string][]string, error) { - if len(hdrsBytes) == 0 && len(h.streamHdrs) == 0 { - return ctx, nil, nil - } - - if len(hdrsBytes) == 0 { - return h.newContext(ctx, h.streamHdrs), h.streamHdrs, nil - } - - // Note that we will parse the headers even if they are not - // used, to check for validity and/or trace context. Also - // note this code was once optimized to avoid the following - // map allocation in cases where the return value would not be - // used. This logic was "is metadata present" or "is auth - // server used". Then we added to this, "is trace propagation - // in use" and simplified this function to always store the - // headers into a temporary map. - h.tmpHdrs = map[string][]string{} - - // Write calls the emitFunc, appending directly into `tmpHdrs`. - if _, err := h.decoder.Write(hdrsBytes); err != nil { - return ctx, nil, err - } - - // Get the global propagator, to extract context. When there - // are no fields, it's a no-op propagator implementation and - // we can skip the allocations inside this block. - carrier := otel.GetTextMapPropagator() - if len(carrier.Fields()) != 0 { - // When there are no fields, it's a no-op - // implementation and we can skip the allocations. - flat := map[string]string{} - for _, key := range carrier.Fields() { - have := h.tmpHdrs[key] - if len(have) > 0 { - flat[key] = have[0] - delete(h.tmpHdrs, key) - } - } - - ctx = carrier.Extract(ctx, propagation.MapCarrier(flat)) - } - - // Add streamHdrs that were not carried in the per-request headers. - for k, v := range h.streamHdrs { - // Note: This is done after the per-request metadata is defined - // in recognition of a potential for duplicated values stemming - // from the Arrow exporter's independent call to the Auth - // extension's GetRequestMetadata(). This paired with the - // headersetter's return of empty-string values means, we would - // end up with an empty-string element for any headersetter - // `from_context` rules b/c the stream uses background context. - // This allows static headers through. - // - // See https://github.com/open-telemetry/opentelemetry-collector/issues/6965 - lk := strings.ToLower(k) - if _, ok := h.tmpHdrs[lk]; !ok { - h.tmpHdrs[lk] = v - } - } - - // Release the temporary copy used in emitFunc(). - newHdrs := h.tmpHdrs - h.tmpHdrs = nil - - // Note: newHdrs is passed to the Auth plugin. Whether - // newHdrs is set in the context depends on h.includeMetadata. - return h.newContext(ctx, newHdrs), newHdrs, nil -} - -// tmpHdrsAppend appends to tmpHdrs, from decoder's emit function. -func (h *headerReceiver) tmpHdrsAppend(hf hpack.HeaderField) { - if h.tmpHdrs != nil { - // We force strings.ToLower to ensure consistency. gRPC itself - // does this and would do the same. - hn := strings.ToLower(hf.Name) - h.tmpHdrs[hn] = append(h.tmpHdrs[hn], hf.Value) - } -} - -func (h *headerReceiver) newContext(ctx context.Context, hdrs map[string][]string) context.Context { - // Retain the Addr/Auth of the stream connection, update the - // per-request metadata from the Arrow batch. - var md client.Metadata - if h.includeMetadata && hdrs != nil { - md = client.NewMetadata(hdrs) - } - return client.NewContext(ctx, client.Info{ - Addr: h.connInfo.Addr, - Auth: h.connInfo.Auth, - Metadata: md, - }) -} - -// logStreamError decides how to log an error. -func (r *Receiver) logStreamError(err error, where string) { - var code codes.Code - var msg string - // gRPC tends to supply status-wrapped errors, so we always - // unpack them. A wrapped Canceled code indicates intentional - // shutdown, which can be due to normal causes (EOF, e.g., - // max-stream-lifetime reached) or unusual causes (Canceled, - // e.g., because the other stream direction reached an error). - if status, ok := status.FromError(err); ok { - code = status.Code() - msg = status.Message() - } else if errors.Is(err, io.EOF) || errors.Is(err, context.Canceled) { - code = codes.Canceled - msg = err.Error() - } else { - code = codes.Internal - msg = err.Error() - } - - if code == codes.Canceled { - r.telemetry.Logger.Debug("arrow stream shutdown", zap.String("message", msg), zap.String("where", where)) - } else { - r.telemetry.Logger.Error("arrow stream error", zap.Int("code", int(code)), zap.String("message", msg), zap.String("where", where)) - } -} - -func gRPCName(desc grpc.ServiceDesc) string { - return netstats.GRPCStreamMethodName(desc, desc.Streams[0]) -} - -var ( - arrowTracesMethod = gRPCName(arrowpb.ArrowTracesService_ServiceDesc) - arrowMetricsMethod = gRPCName(arrowpb.ArrowMetricsService_ServiceDesc) - arrowLogsMethod = gRPCName(arrowpb.ArrowLogsService_ServiceDesc) -) - -func (r *Receiver) ArrowTraces(serverStream arrowpb.ArrowTracesService_ArrowTracesServer) error { - return r.anyStream(serverStream, arrowTracesMethod) -} - -func (r *Receiver) ArrowLogs(serverStream arrowpb.ArrowLogsService_ArrowLogsServer) error { - return r.anyStream(serverStream, arrowLogsMethod) -} - -func (r *Receiver) ArrowMetrics(serverStream arrowpb.ArrowMetricsService_ArrowMetricsServer) error { - return r.anyStream(serverStream, arrowMetricsMethod) -} - -type anyStreamServer interface { - Send(*arrowpb.BatchStatus) error - Recv() (*arrowpb.BatchArrowRecords, error) - grpc.ServerStream -} - -type batchResp struct { - id int64 - err error -} - -func (r *Receiver) recoverErr(retErr *error) { - if err := recover(); err != nil { - // When this happens, the stacktrace is - // important and lost if we don't capture it - // here. - r.telemetry.Logger.Error("panic detail in otel-arrow-adapter", - zap.Reflect("recovered", err), - zap.Stack("stacktrace"), - ) - *retErr = status.Errorf(codes.Internal, "panic in otel-arrow-adapter: %v", err) - } -} - -func (r *Receiver) anyStream(serverStream anyStreamServer, method string) (retErr error) { - streamCtx := serverStream.Context() - ac := r.newConsumer() - - defer func() { - if err := ac.Close(); err != nil { - r.telemetry.Logger.Error("arrow stream close", zap.Error(err)) - } - }() - defer r.recoverErr(&retErr) - - // doneCancel allows an error in the sender/receiver to - // interrupt the corresponding thread. - doneCtx, doneCancel := context.WithCancel(streamCtx) - defer doneCancel() - - // streamErrCh returns up to two errors from the sender and - // receiver threads started below. - streamErrCh := make(chan error, 2) - pendingCh := make(chan batchResp, runtime.NumCPU()) - - // wg is used to ensure this thread returns after both - // sender and recevier threads return. - var sendWG sync.WaitGroup - var recvWG sync.WaitGroup - sendWG.Add(1) - recvWG.Add(1) - - rstream := &receiverStream{ - Receiver: r, - } - - go func() { - var err error - defer recvWG.Done() - defer r.recoverErr(&err) - err = rstream.srvReceiveLoop(doneCtx, serverStream, pendingCh, method, ac) - streamErrCh <- err - }() - - go func() { - var err error - defer sendWG.Done() - defer r.recoverErr(&err) - err = rstream.srvSendLoop(doneCtx, serverStream, &recvWG, pendingCh) - streamErrCh <- err - }() - - // Wait for sender/receiver threads to return before returning. - defer recvWG.Wait() - defer sendWG.Wait() - - select { - case <-doneCtx.Done(): - return status.Error(codes.Canceled, "server stream shutdown") - case retErr = <-streamErrCh: - doneCancel() - return - } -} - -func (r *receiverStream) newInFlightData(ctx context.Context, method string, batchID int64, pendingCh chan<- batchResp) (context.Context, *inFlightData) { - ctx, span := r.tracer.Start(ctx, "otel_arrow_stream_inflight") - - r.inFlightWG.Add(1) - r.recvInFlightRequests.Add(ctx, 1) - id := &inFlightData{ - receiverStream: r, - method: method, - batchID: batchID, - pendingCh: pendingCh, - span: span, - } - id.refs.Add(1) - return ctx, id -} - -// inFlightData is responsible for storing the resources held by one request. -type inFlightData struct { - // Receiver is the owner of the resources held by this object. - *receiverStream - - method string - batchID int64 - pendingCh chan<- batchResp - span trace.Span - - // refs counts the number of goroutines holding this object. - // initially the recvOne() body, on success the - // consumeAndRespond() function. - refs atomic.Int32 - - numAcquired int64 // how many bytes held in the semaphore - numItems int // how many items - uncompSize int64 // uncompressed data size -} - -func (id *inFlightData) recvDone(ctx context.Context, recvErrPtr *error) { - retErr := *recvErrPtr - - if retErr != nil { - // logStreamError because this response will break the stream. - id.logStreamError(retErr, "recv") - id.span.SetStatus(otelcodes.Error, retErr.Error()) - } - - id.anyDone(ctx) -} - -func (id *inFlightData) consumeDone(ctx context.Context, consumeErrPtr *error) { - retErr := *consumeErrPtr - - if retErr != nil { - // debug-level because the error was external from the pipeline. - id.telemetry.Logger.Debug("otel-arrow consume", zap.Error(retErr)) - id.span.SetStatus(otelcodes.Error, retErr.Error()) - } - - id.replyToCaller(retErr) - id.anyDone(ctx) -} - -func (id *inFlightData) replyToCaller(callerErr error) { - id.pendingCh <- batchResp{ - id: id.batchID, - err: callerErr, - } -} - -func (id *inFlightData) anyDone(ctx context.Context) { - // check if there are still refs, in which case leave the in-flight - // counts where they are. - if id.refs.Add(-1) != 0 { - return - } - - id.span.End() - - if id.numAcquired != 0 { - if err := id.boundedQueue.Release(id.numAcquired); err != nil { - id.telemetry.Logger.Error("release error", zap.Error(err)) - } - } - - if id.uncompSize != 0 { - id.recvInFlightBytes.Add(ctx, -id.uncompSize) - } - if id.numItems != 0 { - id.recvInFlightItems.Add(ctx, int64(-id.numItems)) - } - - // The netstats code knows that uncompressed size is - // unreliable for arrow transport, so we instrument it - // directly here. Only the primary direction of transport - // is instrumented this way. - var sized netstats.SizesStruct - sized.Method = id.method - sized.Length = id.uncompSize - id.netReporter.CountReceive(ctx, sized) - - id.recvInFlightRequests.Add(ctx, -1) - id.inFlightWG.Done() -} - -// recvOne begins processing a single Arrow batch. -// -// If an error is encountered before Arrow data is successfully consumed, -// the stream will break and the error will be returned immediately. -// -// If the error is due to authorization, the stream remains unbroken -// and the request fails. -// -// If not enough resources are available, the stream will block (if -// waiting permitted) or break (insufficient waiters). -// -// Assuming success, a new goroutine is created to handle consuming the -// data. -// -// This handles constructing an inFlightData object, which itself -// tracks everything that needs to be used by instrumention when the -// batch finishes. -func (r *receiverStream) recvOne(streamCtx context.Context, serverStream anyStreamServer, hrcv *headerReceiver, pendingCh chan<- batchResp, method string, ac arrowRecord.ConsumerAPI) (retErr error) { - - // Receive a batch corresponding with one ptrace.Traces, pmetric.Metrics, - // or plog.Logs item. - req, err := serverStream.Recv() - - // inflightCtx is carried through into consumeAndProcess on the success path. - inflightCtx, flight := r.newInFlightData(streamCtx, method, req.GetBatchId(), pendingCh) - defer flight.recvDone(inflightCtx, &retErr) - - if err != nil { - if errors.Is(err, io.EOF) { - return status.Error(codes.Canceled, "client stream shutdown") - } else if errors.Is(err, context.Canceled) { - return status.Error(codes.Canceled, "server stream shutdown") - } - // Note: err is directly from gRPC, should already have status. - return err - } - - // Check for optional headers and set the incoming context. - inflightCtx, authHdrs, err := hrcv.combineHeaders(inflightCtx, req.GetHeaders()) - if err != nil { - // Failing to parse the incoming headers breaks the stream. - return status.Errorf(codes.Internal, "arrow metadata error: %v", err) - } - - // start this span after hrcv.combineHeaders returns extracted context. This will allow this span - // to be a part of the data path trace instead of only being included as a child of the stream inflight trace. - inflightCtx, span := r.tracer.Start(inflightCtx, "otel_arrow_stream_recv") - defer span.End() - - // Authorize the request, if configured, prior to acquiring resources. - if r.authServer != nil { - var authErr error - inflightCtx, authErr = r.authServer.Authenticate(inflightCtx, authHdrs) - if authErr != nil { - flight.replyToCaller(status.Error(codes.Unauthenticated, authErr.Error())) - return nil - } - } - - var prevAcquiredBytes int64 - uncompSizeHeaderStr, uncompSizeHeaderFound := authHdrs["otlp-pdata-size"] - if !uncompSizeHeaderFound || len(uncompSizeHeaderStr) == 0 { - // This is a compressed size so make sure to acquire the difference when request is decompressed. - prevAcquiredBytes = int64(proto.Size(req)) - } else { - prevAcquiredBytes, err = strconv.ParseInt(uncompSizeHeaderStr[0], 10, 64) - if err != nil { - return status.Errorf(codes.Internal, "failed to convert string to request size: %v", err) - } - } - - // Use the bounded queue to memory limit based on incoming - // uncompressed request size and waiters. Acquire will fail - // immediately if there are too many waiters, or will - // otherwise block until timeout or enough memory becomes - // available. - err = r.boundedQueue.Acquire(inflightCtx, prevAcquiredBytes) - if err != nil { - return status.Errorf(codes.ResourceExhausted, "otel-arrow bounded queue: %v", err) - } - flight.numAcquired = prevAcquiredBytes - - data, numItems, uncompSize, err := r.consumeBatch(ac, req) - - if err != nil { - if errors.Is(err, arrowRecord.ErrConsumerMemoryLimit) { - return status.Errorf(codes.ResourceExhausted, "otel-arrow decode: %v", err) - } - return status.Errorf(codes.Internal, "otel-arrow decode: %v", err) - } - - flight.uncompSize = uncompSize - flight.numItems = numItems - - r.recvInFlightBytes.Add(inflightCtx, uncompSize) - r.recvInFlightItems.Add(inflightCtx, int64(numItems)) - - numAcquired, err := r.acquireAdditionalBytes(inflightCtx, prevAcquiredBytes, uncompSize, hrcv.connInfo.Addr, uncompSizeHeaderFound) - - flight.numAcquired = numAcquired - if err != nil { - return status.Errorf(codes.ResourceExhausted, "otel-arrow bounded queue re-acquire: %v", err) - } - - // Recognize that the request is still in-flight via consumeAndRespond() - flight.refs.Add(1) - - // consumeAndRespond consumes the data and returns control to the sender loop. - go r.consumeAndRespond(inflightCtx, data, flight) - - return nil -} - -// consumeAndRespond finishes the span started in recvOne and logs the -// result after invoking the pipeline to consume the data. -func (r *Receiver) consumeAndRespond(ctx context.Context, data any, flight *inFlightData) { - var err error - defer flight.consumeDone(ctx, &err) - - // recoverErr is a special function because it recovers panics, so we - // keep it in a separate defer than the processing above, which will - // run after the panic is recovered into an ordinary error. - defer r.recoverErr(&err) - - err = r.consumeData(ctx, data, flight) -} - -// srvReceiveLoop repeatedly receives one batch of data. -func (r *receiverStream) srvReceiveLoop(ctx context.Context, serverStream anyStreamServer, pendingCh chan<- batchResp, method string, ac arrowRecord.ConsumerAPI) (retErr error) { - hrcv := newHeaderReceiver(ctx, r.authServer, r.gsettings.IncludeMetadata) - for { - select { - case <-ctx.Done(): - return status.Error(codes.Canceled, "server stream shutdown") - default: - if err := r.recvOne(ctx, serverStream, hrcv, pendingCh, method, ac); err != nil { - return err - } - } - } -} - -// srvReceiveLoop repeatedly sends one batch data response. -func (r *receiverStream) sendOne(serverStream anyStreamServer, resp batchResp) error { - // Note: Statuses can be batched, but we do not take - // advantage of this feature. - bs := &arrowpb.BatchStatus{ - BatchId: resp.id, - } - if resp.err == nil { - bs.StatusCode = arrowpb.StatusCode_OK - } else { - // Generally, code in the receiver should use - // status.Errorf(codes.XXX, ...) so that we take the - // first branch. - if gsc, ok := status.FromError(resp.err); ok { - bs.StatusCode = arrowpb.StatusCode(gsc.Code()) - bs.StatusMessage = gsc.Message() - } else { - // Ideally, we don't take this branch because all code uses - // gRPC status constructors and we've taken the branch above. - // - // This is a fallback for several broad categories of error. - bs.StatusMessage = resp.err.Error() - - switch { - case consumererror.IsPermanent(resp.err): - // Some kind of pipeline error, somewhere downstream. - r.telemetry.Logger.Error("arrow data error", zap.Error(resp.err)) - bs.StatusCode = arrowpb.StatusCode_INVALID_ARGUMENT - default: - // Probably a pipeline error, retryable. - r.telemetry.Logger.Debug("arrow consumer error", zap.Error(resp.err)) - bs.StatusCode = arrowpb.StatusCode_UNAVAILABLE - } - } - } - - if err := serverStream.Send(bs); err != nil { - // logStreamError because this response will break the stream. - r.logStreamError(err, "send") - return err - } - - return nil -} - -func (r *receiverStream) flushSender(serverStream anyStreamServer, recvWG *sync.WaitGroup, pendingCh <-chan batchResp) error { - // wait to ensure no more items are accepted - recvWG.Wait() - - // wait for all responses to be sent - r.inFlightWG.Wait() - - for { - select { - case resp := <-pendingCh: - if err := r.sendOne(serverStream, resp); err != nil { - return err - } - default: - // Currently nothing left in pendingCh. - return nil - } - } -} - -func (r *receiverStream) srvSendLoop(ctx context.Context, serverStream anyStreamServer, recvWG *sync.WaitGroup, pendingCh <-chan batchResp) error { - for { - select { - case <-ctx.Done(): - return r.flushSender(serverStream, recvWG, pendingCh) - case resp := <-pendingCh: - if err := r.sendOne(serverStream, resp); err != nil { - return err - } - } - } -} - -// consumeBatch applies the batch to the Arrow Consumer, returns a -// slice of pdata objects of the corresponding data type as `any`. -// along with the number of items and true uncompressed size. -func (r *Receiver) consumeBatch(arrowConsumer arrowRecord.ConsumerAPI, records *arrowpb.BatchArrowRecords) (retData any, numItems int, uncompSize int64, retErr error) { - - payloads := records.GetArrowPayloads() - if len(payloads) == 0 { - return nil, 0, 0, nil - } - - switch payloads[0].Type { - case arrowpb.ArrowPayloadType_UNIVARIATE_METRICS: - if r.Metrics() == nil { - return nil, 0, 0, status.Error(codes.Unimplemented, "metrics service not available") - } - var sizer pmetric.ProtoMarshaler - - data, err := arrowConsumer.MetricsFrom(records) - if err == nil { - for _, metrics := range data { - numItems += metrics.DataPointCount() - uncompSize += int64(sizer.MetricsSize(metrics)) - } - } - retData = data - retErr = err - - case arrowpb.ArrowPayloadType_LOGS: - if r.Logs() == nil { - return nil, 0, 0, status.Error(codes.Unimplemented, "logs service not available") - } - var sizer plog.ProtoMarshaler - - data, err := arrowConsumer.LogsFrom(records) - if err == nil { - for _, logs := range data { - numItems += logs.LogRecordCount() - uncompSize += int64(sizer.LogsSize(logs)) - } - } - retData = data - retErr = err - - case arrowpb.ArrowPayloadType_SPANS: - if r.Traces() == nil { - return nil, 0, 0, status.Error(codes.Unimplemented, "traces service not available") - } - var sizer ptrace.ProtoMarshaler - - data, err := arrowConsumer.TracesFrom(records) - if err == nil { - for _, traces := range data { - numItems += traces.SpanCount() - uncompSize += int64(sizer.TracesSize(traces)) - } - } - retData = data - retErr = err - - default: - retErr = ErrUnrecognizedPayload - } - - return retData, numItems, uncompSize, retErr -} - -// consumeData invokes the next pipeline consumer for a received batch of data. -// it uses the standard OTel collector instrumentation (receiverhelper.ObsReport). -// -// if any errors are permanent, returns a permanent error. -func (r *Receiver) consumeData(ctx context.Context, data any, flight *inFlightData) (retErr error) { - oneOp := func(err error) { - retErr = multierr.Append(retErr, err) - } - var final func(context.Context, string, int, error) - - switch items := data.(type) { - case []pmetric.Metrics: - ctx = r.obsrecv.StartMetricsOp(ctx) - for _, metrics := range items { - oneOp(r.Metrics().ConsumeMetrics(ctx, metrics)) - } - final = r.obsrecv.EndMetricsOp - - case []plog.Logs: - ctx = r.obsrecv.StartLogsOp(ctx) - for _, logs := range items { - oneOp(r.Logs().ConsumeLogs(ctx, logs)) - } - final = r.obsrecv.EndLogsOp - - case []ptrace.Traces: - ctx = r.obsrecv.StartTracesOp(ctx) - for _, traces := range items { - oneOp(r.Traces().ConsumeTraces(ctx, traces)) - } - final = r.obsrecv.EndTracesOp - - default: - retErr = ErrUnrecognizedPayload - } - if final != nil { - final(ctx, streamFormat, flight.numItems, retErr) - } - return retErr -} - -func (r *Receiver) acquireAdditionalBytes(ctx context.Context, prevAcquired, uncompSize int64, addr net.Addr, uncompSizeHeaderFound bool) (int64, error) { - diff := uncompSize - prevAcquired - - if diff == 0 { - return uncompSize, nil - } - - if uncompSizeHeaderFound { - var clientAddr string - if addr != nil { - clientAddr = addr.String() - } - // a mismatch between header set by exporter and the uncompSize just calculated. - r.telemetry.Logger.Debug("mismatch between uncompressed size in receiver and otlp-pdata-size header", - zap.String("client-address", clientAddr), - zap.Int("uncompsize", int(uncompSize)), - zap.Int("otlp-pdata-size", int(prevAcquired)), - ) - } else if diff < 0 { - // proto.Size() on compressed request was greater than pdata uncompressed size. - r.telemetry.Logger.Debug("uncompressed size is less than compressed size", - zap.Int("uncompressed", int(uncompSize)), - zap.Int("compressed", int(prevAcquired)), - ) - } - - if diff < 0 { - // If the difference is negative, release the overage. - if err := r.boundedQueue.Release(-diff); err != nil { - return 0, err - } - } else { - // Release previously acquired bytes to prevent deadlock and - // reacquire the uncompressed size we just calculated. - if err := r.boundedQueue.Release(prevAcquired); err != nil { - return 0, err - } - if err := r.boundedQueue.Acquire(ctx, uncompSize); err != nil { - return 0, err - } - } - return uncompSize, nil -} diff --git a/collector/receiver/otelarrowreceiver/internal/arrow/arrow_test.go b/collector/receiver/otelarrowreceiver/internal/arrow/arrow_test.go deleted file mode 100644 index 13838690..00000000 --- a/collector/receiver/otelarrowreceiver/internal/arrow/arrow_test.go +++ /dev/null @@ -1,1361 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package arrow - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "strconv" - "strings" - "sync" - "testing" - "time" - - arrowpb "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1" - arrowCollectorMock "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1/mock" - "github.com/open-telemetry/otel-arrow/collector/netstats" - "github.com/open-telemetry/otel-arrow/collector/testdata" - arrowRecord "github.com/open-telemetry/otel-arrow/pkg/otel/arrow_record" - arrowRecordMock "github.com/open-telemetry/otel-arrow/pkg/otel/arrow_record/mock" - otelAssert "github.com/open-telemetry/otel-arrow/pkg/otel/assert" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/client" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/extension/auth" - "go.opentelemetry.io/collector/pdata/plog" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.opentelemetry.io/collector/pdata/ptrace" - "go.opentelemetry.io/collector/receiver" - "go.opentelemetry.io/collector/receiver/receiverhelper" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/propagation" - "go.opentelemetry.io/otel/trace" - "go.uber.org/mock/gomock" - "go.uber.org/zap/zaptest" - "golang.org/x/net/http2/hpack" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - - "github.com/open-telemetry/otel-arrow/collector/admission" - "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/arrow/mock" -) - -func defaultBQ() *admission.BoundedQueue { - return admission.NewBoundedQueue(int64(100000), int64(10)) -} - -type compareJSONTraces struct{ ptrace.Traces } -type compareJSONMetrics struct{ pmetric.Metrics } -type compareJSONLogs struct{ plog.Logs } - -func (c compareJSONTraces) MarshalJSON() ([]byte, error) { - var m ptrace.JSONMarshaler - return m.MarshalTraces(c.Traces) -} - -func (c compareJSONMetrics) MarshalJSON() ([]byte, error) { - var m pmetric.JSONMarshaler - return m.MarshalMetrics(c.Metrics) -} - -func (c compareJSONLogs) MarshalJSON() ([]byte, error) { - var m plog.JSONMarshaler - return m.MarshalLogs(c.Logs) -} - -type consumeResult struct { - Ctx context.Context - Data any -} - -type commonTestCase struct { - *testing.T - - ctrl *gomock.Controller - cancel context.CancelFunc - telset component.TelemetrySettings - consumers mockConsumers - stream *arrowCollectorMock.MockArrowTracesService_ArrowTracesServer - receive chan recvResult - consume chan consumeResult - streamErr chan error - - // testProducer is for convenience -- not thread safe, see copyBatch(). - testProducer *arrowRecord.Producer - - ctxCall *gomock.Call - recvCall *gomock.Call -} - -type testChannel interface { - onConsume() error -} - -type healthyTestChannel struct{} - -func (healthyTestChannel) onConsume() error { - return nil -} - -type unhealthyTestChannel struct{} - -func (unhealthyTestChannel) onConsume() error { - return status.Errorf(codes.Unavailable, "consumer unhealthy") -} - -type recvResult struct { - payload *arrowpb.BatchArrowRecords - err error -} - -type mockConsumers struct { - traces *mock.MockTraces - logs *mock.MockLogs - metrics *mock.MockMetrics - - tracesCall *gomock.Call - logsCall *gomock.Call - metricsCall *gomock.Call -} - -func newTestTelemetry(t *testing.T) component.TelemetrySettings { - telset := componenttest.NewNopTelemetrySettings() - telset.Logger = zaptest.NewLogger(t) - return telset -} - -func (ctc *commonTestCase) putBatch(payload *arrowpb.BatchArrowRecords, err error) { - ctc.receive <- recvResult{ - payload: payload, - err: err, - } -} - -func (ctc *commonTestCase) doAndReturnGetBatch(ctx context.Context) func() (*arrowpb.BatchArrowRecords, error) { - return func() (*arrowpb.BatchArrowRecords, error) { - select { - case <-ctx.Done(): - return nil, ctx.Err() - case r, ok := <-ctc.receive: - if !ok { - return nil, io.EOF - } - return r.payload, r.err - } - } -} - -func (ctc *commonTestCase) doAndReturnConsumeTraces(tc testChannel) func(ctx context.Context, traces ptrace.Traces) error { - return func(ctx context.Context, traces ptrace.Traces) error { - ctc.consume <- consumeResult{ - Ctx: ctx, - Data: traces, - } - return tc.onConsume() - } -} - -func (ctc *commonTestCase) doAndReturnConsumeMetrics(tc testChannel) func(ctx context.Context, metrics pmetric.Metrics) error { - return func(ctx context.Context, metrics pmetric.Metrics) error { - ctc.consume <- consumeResult{ - Ctx: ctx, - Data: metrics, - } - return tc.onConsume() - } -} - -func (ctc *commonTestCase) doAndReturnConsumeLogs(tc testChannel) func(ctx context.Context, logs plog.Logs) error { - return func(ctx context.Context, logs plog.Logs) error { - ctc.consume <- consumeResult{ - Ctx: ctx, - Data: logs, - } - return tc.onConsume() - } -} - -func newMockConsumers(ctrl *gomock.Controller) mockConsumers { - mc := mockConsumers{ - traces: mock.NewMockTraces(ctrl), - logs: mock.NewMockLogs(ctrl), - metrics: mock.NewMockMetrics(ctrl), - } - mc.traces.EXPECT().Capabilities().Times(0) - mc.tracesCall = mc.traces.EXPECT().ConsumeTraces( - gomock.Any(), - gomock.Any(), - ).Times(0) - mc.logs.EXPECT().Capabilities().Times(0) - mc.logsCall = mc.logs.EXPECT().ConsumeLogs( - gomock.Any(), - gomock.Any(), - ).Times(0) - mc.metrics.EXPECT().Capabilities().Times(0) - mc.metricsCall = mc.metrics.EXPECT().ConsumeMetrics( - gomock.Any(), - gomock.Any(), - ).Times(0) - return mc -} - -func (m mockConsumers) Traces() consumer.Traces { - return m.traces -} - -func (m mockConsumers) Logs() consumer.Logs { - return m.logs -} -func (m mockConsumers) Metrics() consumer.Metrics { - return m.metrics -} - -var _ Consumers = mockConsumers{} - -func newCommonTestCase(t *testing.T, tc testChannel) *commonTestCase { - ctrl := gomock.NewController(t) - stream := arrowCollectorMock.NewMockArrowTracesService_ArrowTracesServer(ctrl) - - ctx, cancel := context.WithCancel(context.Background()) - ctx = metadata.NewIncomingContext(ctx, metadata.MD{ - "stream_ctx": []string{"per-request"}, - }) - - ctc := &commonTestCase{ - T: t, - ctrl: ctrl, - cancel: cancel, - telset: newTestTelemetry(t), - consumers: newMockConsumers(ctrl), - stream: stream, - receive: make(chan recvResult), - consume: make(chan consumeResult), - streamErr: make(chan error), - testProducer: arrowRecord.NewProducer(), - ctxCall: stream.EXPECT().Context().Times(0), - recvCall: stream.EXPECT().Recv().Times(0), - } - - ctc.ctxCall.AnyTimes().Return(ctx) - ctc.recvCall.AnyTimes().DoAndReturn(ctc.doAndReturnGetBatch(ctx)) - ctc.consumers.tracesCall.AnyTimes().DoAndReturn(ctc.doAndReturnConsumeTraces(tc)) - ctc.consumers.logsCall.AnyTimes().DoAndReturn(ctc.doAndReturnConsumeLogs(tc)) - ctc.consumers.metricsCall.AnyTimes().DoAndReturn(ctc.doAndReturnConsumeMetrics(tc)) - return ctc -} - -func (ctc *commonTestCase) cancelAndWait() error { - ctc.cancel() - return ctc.wait() -} - -func (ctc *commonTestCase) wait() error { - return <-ctc.streamErr -} - -func statusOKFor(batchID int64) *arrowpb.BatchStatus { - return &arrowpb.BatchStatus{ - BatchId: batchID, - StatusCode: arrowpb.StatusCode_OK, - } -} - -func statusUnavailableFor(batchID int64, msg string) *arrowpb.BatchStatus { - return &arrowpb.BatchStatus{ - BatchId: batchID, - StatusCode: arrowpb.StatusCode_UNAVAILABLE, - StatusMessage: msg, - } -} - -func (ctc *commonTestCase) newRealConsumer() arrowRecord.ConsumerAPI { - mock := arrowRecordMock.NewMockConsumerAPI(ctc.ctrl) - cons := arrowRecord.NewConsumer() - - mock.EXPECT().Close().Times(1).Return(nil) - mock.EXPECT().TracesFrom(gomock.Any()).AnyTimes().DoAndReturn(cons.TracesFrom) - mock.EXPECT().MetricsFrom(gomock.Any()).AnyTimes().DoAndReturn(cons.MetricsFrom) - mock.EXPECT().LogsFrom(gomock.Any()).AnyTimes().DoAndReturn(cons.LogsFrom) - - return mock -} - -func (ctc *commonTestCase) newErrorConsumer() arrowRecord.ConsumerAPI { - mock := arrowRecordMock.NewMockConsumerAPI(ctc.ctrl) - - mock.EXPECT().Close().Times(1).Return(nil) - mock.EXPECT().TracesFrom(gomock.Any()).AnyTimes().Return(nil, fmt.Errorf("test invalid error")) - mock.EXPECT().MetricsFrom(gomock.Any()).AnyTimes().Return(nil, fmt.Errorf("test invalid error")) - mock.EXPECT().LogsFrom(gomock.Any()).AnyTimes().Return(nil, fmt.Errorf("test invalid error")) - - return mock -} - -func (ctc *commonTestCase) newOOMConsumer() arrowRecord.ConsumerAPI { - mock := arrowRecordMock.NewMockConsumerAPI(ctc.ctrl) - - mock.EXPECT().Close().Times(1).Return(nil) - mock.EXPECT().TracesFrom(gomock.Any()).AnyTimes().Return(nil, fmt.Errorf("test oom error %w", arrowRecord.ErrConsumerMemoryLimit)) - mock.EXPECT().MetricsFrom(gomock.Any()).AnyTimes().Return(nil, fmt.Errorf("test oom error %w", arrowRecord.ErrConsumerMemoryLimit)) - mock.EXPECT().LogsFrom(gomock.Any()).AnyTimes().Return(nil, fmt.Errorf("test oom error %w", arrowRecord.ErrConsumerMemoryLimit)) - - return mock -} - -func (ctc *commonTestCase) start(newConsumer func() arrowRecord.ConsumerAPI, bq *admission.BoundedQueue, opts ...func(*configgrpc.ServerConfig, *auth.Server)) { - var authServer auth.Server - var gsettings configgrpc.ServerConfig - for _, gf := range opts { - gf(&gsettings, &authServer) - } - rc := receiver.Settings{ - TelemetrySettings: ctc.telset, - BuildInfo: component.NewDefaultBuildInfo(), - } - obsrecv, err := receiverhelper.NewObsReport(receiverhelper.ObsReportSettings{ - ReceiverID: component.NewID(component.MustNewType("arrowtest")), - Transport: "grpc", - ReceiverCreateSettings: rc, - }) - require.NoError(ctc.T, err) - - rcvr, err := New( - ctc.consumers, - rc, - obsrecv, - gsettings, - authServer, - newConsumer, - bq, - netstats.Noop{}, - ) - require.NoError(ctc.T, err) - go func() { - ctc.streamErr <- rcvr.ArrowTraces(ctc.stream) - }() -} - -func requireCanceledStatus(t *testing.T, err error) { - requireStatus(t, codes.Canceled, err) -} - -func requireUnavailableStatus(t *testing.T, err error) { - requireStatus(t, codes.Unavailable, err) -} - -func requireInternalStatus(t *testing.T, err error) { - requireStatus(t, codes.Internal, err) -} - -func requireExhaustedStatus(t *testing.T, err error) { - requireStatus(t, codes.ResourceExhausted, err) -} - -func requireStatus(t *testing.T, code codes.Code, err error) { - require.Error(t, err) - status, ok := status.FromError(err) - require.True(t, ok, "is status-wrapped %v", err) - require.Equal(t, code, status.Code()) -} - -func TestBoundedQueueWithPdataHeaders(t *testing.T) { - var sizer ptrace.ProtoMarshaler - stdTesting := otelAssert.NewStdUnitTest(t) - pdataSizeTenTraces := sizer.TracesSize(testdata.GenerateTraces(10)) - defaultBoundedQueueLimit := int64(100000) - tests := []struct { - name string - numTraces int - includePdataHeader bool - pdataSize string - rejected bool - }{ - { - name: "no header compressed greater than uncompressed", - numTraces: 10, - }, - { - name: "no header compressed less than uncompressed", - numTraces: 100, - }, - { - name: "pdata header less than uncompressedSize", - numTraces: 10, - pdataSize: strconv.Itoa(pdataSizeTenTraces / 2), - includePdataHeader: true, - }, - { - name: "pdata header equal uncompressedSize", - numTraces: 10, - pdataSize: strconv.Itoa(pdataSizeTenTraces), - includePdataHeader: true, - }, - { - name: "pdata header greater than uncompressedSize", - numTraces: 10, - pdataSize: strconv.Itoa(pdataSizeTenTraces * 2), - includePdataHeader: true, - }, - { - name: "no header compressed accepted uncompressed rejected", - numTraces: 100, - rejected: true, - }, - { - name: "pdata header accepted uncompressed rejected", - numTraces: 100, - rejected: true, - pdataSize: strconv.Itoa(pdataSizeTenTraces), - includePdataHeader: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - td := testdata.GenerateTraces(tt.numTraces) - batch, err := ctc.testProducer.BatchArrowRecordsFromTraces(td) - require.NoError(t, err) - if tt.includePdataHeader { - var hpb bytes.Buffer - hpe := hpack.NewEncoder(&hpb) - err = hpe.WriteField(hpack.HeaderField{ - Name: "otlp-pdata-size", - Value: tt.pdataSize, - }) - assert.NoError(t, err) - batch.Headers = make([]byte, hpb.Len()) - copy(batch.Headers, hpb.Bytes()) - } - - var bq *admission.BoundedQueue - if tt.rejected { - ctc.stream.EXPECT().Send(statusOKFor(batch.BatchId)).Times(0) - bq = admission.NewBoundedQueue(int64(sizer.TracesSize(td)-100), 10) - } else { - ctc.stream.EXPECT().Send(statusOKFor(batch.BatchId)).Times(1).Return(nil) - bq = admission.NewBoundedQueue(defaultBoundedQueueLimit, 10) - } - - ctc.start(ctc.newRealConsumer, bq) - ctc.putBatch(batch, nil) - - if tt.rejected { - requireExhaustedStatus(t, ctc.wait()) - } else { - data := <-ctc.consume - actualTD := data.Data.(ptrace.Traces) - otelAssert.Equiv(stdTesting, []json.Marshaler{ - compareJSONTraces{td}, - }, []json.Marshaler{ - compareJSONTraces{actualTD}, - }) - requireCanceledStatus(t, ctc.cancelAndWait()) - } - }) - } -} - -func TestReceiverTraces(t *testing.T) { - stdTesting := otelAssert.NewStdUnitTest(t) - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - td := testdata.GenerateTraces(2) - batch, err := ctc.testProducer.BatchArrowRecordsFromTraces(td) - require.NoError(t, err) - - ctc.stream.EXPECT().Send(statusOKFor(batch.BatchId)).Times(1).Return(nil) - - ctc.start(ctc.newRealConsumer, defaultBQ()) - ctc.putBatch(batch, nil) - - otelAssert.Equiv(stdTesting, []json.Marshaler{ - compareJSONTraces{td}, - }, []json.Marshaler{ - compareJSONTraces{(<-ctc.consume).Data.(ptrace.Traces)}, - }) - - err = ctc.cancelAndWait() - requireCanceledStatus(t, err) -} - -func TestReceiverLogs(t *testing.T) { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - ld := testdata.GenerateLogs(2) - batch, err := ctc.testProducer.BatchArrowRecordsFromLogs(ld) - require.NoError(t, err) - - ctc.stream.EXPECT().Send(statusOKFor(batch.BatchId)).Times(1).Return(nil) - - ctc.start(ctc.newRealConsumer, defaultBQ()) - ctc.putBatch(batch, nil) - - assert.EqualValues(t, []json.Marshaler{compareJSONLogs{ld}}, []json.Marshaler{compareJSONLogs{(<-ctc.consume).Data.(plog.Logs)}}) - - err = ctc.cancelAndWait() - requireCanceledStatus(t, err) -} - -func TestReceiverMetrics(t *testing.T) { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - stdTesting := otelAssert.NewStdUnitTest(t) - - md := testdata.GenerateMetrics(2) - batch, err := ctc.testProducer.BatchArrowRecordsFromMetrics(md) - require.NoError(t, err) - - ctc.stream.EXPECT().Send(statusOKFor(batch.BatchId)).Times(1).Return(nil) - - ctc.start(ctc.newRealConsumer, defaultBQ()) - ctc.putBatch(batch, nil) - - otelAssert.Equiv(stdTesting, []json.Marshaler{ - compareJSONMetrics{md}, - }, []json.Marshaler{ - compareJSONMetrics{(<-ctc.consume).Data.(pmetric.Metrics)}, - }) - - err = ctc.cancelAndWait() - requireCanceledStatus(t, err) -} - -func TestReceiverRecvError(t *testing.T) { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - ctc.start(ctc.newRealConsumer, defaultBQ()) - - ctc.putBatch(nil, fmt.Errorf("test recv error")) - - err := ctc.wait() - require.Error(t, err) - require.Contains(t, err.Error(), "test recv error") -} - -func TestReceiverSendError(t *testing.T) { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - ld := testdata.GenerateLogs(2) - batch, err := ctc.testProducer.BatchArrowRecordsFromLogs(ld) - require.NoError(t, err) - - ctc.stream.EXPECT().Send(statusOKFor(batch.BatchId)).Times(1).Return(status.Errorf(codes.Unavailable, "test send error")) - - ctc.start(ctc.newRealConsumer, defaultBQ()) - ctc.putBatch(batch, nil) - - assert.EqualValues(t, ld, (<-ctc.consume).Data) - - start := time.Now() - for time.Since(start) < 10*time.Second { - if ctc.ctrl.Satisfied() { - break - } - time.Sleep(time.Second) - } - - // Release the receiver -- the sender has seen an error by - // now and should return the stream. (Oddly, gRPC has no way - // to signal the receive call to fail using context.) - close(ctc.receive) - err = ctc.wait() - requireUnavailableStatus(t, err) -} - -func TestReceiverConsumeError(t *testing.T) { - stdTesting := otelAssert.NewStdUnitTest(t) - - data := []any{ - testdata.GenerateTraces(2), - testdata.GenerateMetrics(2), - testdata.GenerateLogs(2), - } - - for _, item := range data { - tc := unhealthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - var batch *arrowpb.BatchArrowRecords - var err error - switch input := item.(type) { - case ptrace.Traces: - batch, err = ctc.testProducer.BatchArrowRecordsFromTraces(input) - case plog.Logs: - batch, err = ctc.testProducer.BatchArrowRecordsFromLogs(input) - case pmetric.Metrics: - batch, err = ctc.testProducer.BatchArrowRecordsFromMetrics(input) - default: - panic(input) - } - require.NoError(t, err) - - batch = copyBatch(batch) - - ctc.stream.EXPECT().Send(statusUnavailableFor(batch.BatchId, "consumer unhealthy")).Times(1).Return(nil) - - ctc.start(ctc.newRealConsumer, defaultBQ()) - - ctc.putBatch(batch, nil) - - switch input := item.(type) { - case ptrace.Traces: - otelAssert.Equiv(stdTesting, []json.Marshaler{ - compareJSONTraces{input}, - }, []json.Marshaler{ - compareJSONTraces{(<-ctc.consume).Data.(ptrace.Traces)}, - }) - case plog.Logs: - otelAssert.Equiv(stdTesting, []json.Marshaler{ - compareJSONLogs{input}, - }, []json.Marshaler{ - compareJSONLogs{(<-ctc.consume).Data.(plog.Logs)}, - }) - case pmetric.Metrics: - otelAssert.Equiv(stdTesting, []json.Marshaler{ - compareJSONMetrics{input}, - }, []json.Marshaler{ - compareJSONMetrics{(<-ctc.consume).Data.(pmetric.Metrics)}, - }) - } - - err = ctc.cancelAndWait() - requireCanceledStatus(t, err) - } -} - -func TestReceiverInvalidData(t *testing.T) { - data := []any{ - testdata.GenerateTraces(2), - testdata.GenerateMetrics(2), - testdata.GenerateLogs(2), - } - - for _, item := range data { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - var batch *arrowpb.BatchArrowRecords - var err error - switch input := item.(type) { - case ptrace.Traces: - batch, err = ctc.testProducer.BatchArrowRecordsFromTraces(input) - case plog.Logs: - batch, err = ctc.testProducer.BatchArrowRecordsFromLogs(input) - case pmetric.Metrics: - batch, err = ctc.testProducer.BatchArrowRecordsFromMetrics(input) - default: - panic(input) - } - require.NoError(t, err) - - batch = copyBatch(batch) - - // newErrorConsumer determines the internal error in decoding above - ctc.start(ctc.newErrorConsumer, defaultBQ()) - ctc.putBatch(batch, nil) - - err = ctc.wait() - requireInternalStatus(t, err) - } -} - -func TestReceiverMemoryLimit(t *testing.T) { - data := []any{ - testdata.GenerateTraces(2), - testdata.GenerateMetrics(2), - testdata.GenerateLogs(2), - } - - for _, item := range data { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - var batch *arrowpb.BatchArrowRecords - var err error - switch input := item.(type) { - case ptrace.Traces: - batch, err = ctc.testProducer.BatchArrowRecordsFromTraces(input) - case plog.Logs: - batch, err = ctc.testProducer.BatchArrowRecordsFromLogs(input) - case pmetric.Metrics: - batch, err = ctc.testProducer.BatchArrowRecordsFromMetrics(input) - default: - panic(input) - } - require.NoError(t, err) - - batch = copyBatch(batch) - - // The Recv() returns an error, there are no Send() calls. - - ctc.start(ctc.newOOMConsumer, defaultBQ()) - ctc.putBatch(batch, nil) - - err = ctc.wait() - requireExhaustedStatus(t, err) - } -} - -func copyBatch(in *arrowpb.BatchArrowRecords) *arrowpb.BatchArrowRecords { - // Because Arrow-IPC uses zero copy, we have to copy inside the test - // instead of sharing pointers to BatchArrowRecords. - - hcpy := make([]byte, len(in.Headers)) - copy(hcpy, in.Headers) - - pays := make([]*arrowpb.ArrowPayload, len(in.ArrowPayloads)) - - for i, inp := range in.ArrowPayloads { - rcpy := make([]byte, len(inp.Record)) - copy(rcpy, inp.Record) - pays[i] = &arrowpb.ArrowPayload{ - SchemaId: inp.SchemaId, - Type: inp.Type, - Record: rcpy, - } - } - - return &arrowpb.BatchArrowRecords{ - BatchId: in.BatchId, - Headers: hcpy, - ArrowPayloads: pays, - } -} - -func TestReceiverEOF(t *testing.T) { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - stdTesting := otelAssert.NewStdUnitTest(t) - - // send a sequence of data then simulate closing the connection. - const times = 10 - - var actualData []ptrace.Traces - var expectData []ptrace.Traces - - ctc.stream.EXPECT().Send(gomock.Any()).Times(times).Return(nil) - - ctc.start(ctc.newRealConsumer, defaultBQ()) - - go func() { - for i := 0; i < times; i++ { - td := testdata.GenerateTraces(2) - expectData = append(expectData, td) - - batch, err := ctc.testProducer.BatchArrowRecordsFromTraces(td) - require.NoError(t, err) - - batch = copyBatch(batch) - - ctc.putBatch(batch, nil) - } - close(ctc.receive) - }() - - var wg sync.WaitGroup - wg.Add(1) - - go func() { - err := ctc.wait() - // EOF is treated the same as Canceled. - requireCanceledStatus(t, err) - wg.Done() - }() - - for i := 0; i < times; i++ { - actualData = append(actualData, (<-ctc.consume).Data.(ptrace.Traces)) - } - - assert.Equal(t, len(expectData), len(actualData)) - - for i := 0; i < len(expectData); i++ { - otelAssert.Equiv(stdTesting, []json.Marshaler{ - compareJSONTraces{expectData[i]}, - }, []json.Marshaler{ - compareJSONTraces{actualData[i]}, - }) - } - - wg.Wait() -} - -func TestReceiverHeadersNoAuth(t *testing.T) { - t.Run("include", func(t *testing.T) { testReceiverHeaders(t, true) }) - t.Run("noinclude", func(t *testing.T) { testReceiverHeaders(t, false) }) -} - -func testReceiverHeaders(t *testing.T, includeMeta bool) { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - expectData := []map[string][]string{ - {"k1": []string{"v1"}}, - nil, - {"k2": []string{"v2"}, "k3": []string{"v3"}}, - nil, - {"k1": []string{"v5"}}, - {"k1": []string{"v1"}, "k3": []string{"v2", "v3", "v4"}}, - nil, - } - - ctc.stream.EXPECT().Send(gomock.Any()).Times(len(expectData)).Return(nil) - - ctc.start(ctc.newRealConsumer, defaultBQ(), func(gsettings *configgrpc.ServerConfig, _ *auth.Server) { - gsettings.IncludeMetadata = includeMeta - }) - - go func() { - var hpb bytes.Buffer - hpe := hpack.NewEncoder(&hpb) - - for _, md := range expectData { - td := testdata.GenerateTraces(2) - - batch, err := ctc.testProducer.BatchArrowRecordsFromTraces(td) - require.NoError(t, err) - - batch = copyBatch(batch) - - if len(md) != 0 { - hpb.Reset() - for key, vals := range md { - for _, val := range vals { - err := hpe.WriteField(hpack.HeaderField{ - Name: key, - Value: val, - }) - require.NoError(t, err) - } - } - - batch.Headers = make([]byte, hpb.Len()) - copy(batch.Headers, hpb.Bytes()) - } - ctc.putBatch(batch, nil) - } - close(ctc.receive) - }() - - var wg sync.WaitGroup - wg.Add(1) - - go func() { - err := ctc.wait() - // EOF is treated the same as Canceled. - requireCanceledStatus(t, err) - wg.Done() - }() - - for _, expect := range expectData { - info := client.FromContext((<-ctc.consume).Ctx) - - // The static stream context contains one extra variable. - if expect == nil { - expect = map[string][]string{} - } - expect["stream_ctx"] = []string{"per-request"} - - for key, vals := range expect { - if includeMeta { - require.Equal(t, vals, info.Metadata.Get(key)) - } else { - require.Equal(t, []string(nil), info.Metadata.Get(key)) - } - } - } - - wg.Wait() -} - -func TestReceiverCancel(t *testing.T) { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - ctc.cancel() - ctc.start(ctc.newRealConsumer, defaultBQ()) - - err := ctc.wait() - requireCanceledStatus(t, err) -} - -func requireContainsAll(t *testing.T, md client.Metadata, exp map[string][]string) { - for key, vals := range exp { - require.Equal(t, vals, md.Get(key)) - } -} - -func requireContainsNone(t *testing.T, md client.Metadata, exp map[string][]string) { - for key := range exp { - require.Equal(t, []string(nil), md.Get(key)) - } -} - -func TestHeaderReceiverStreamContextOnly(t *testing.T) { - expect := map[string][]string{ - "K": {"k1", "k2"}, - "L": {"l1"}, - } - - ctx := metadata.NewIncomingContext(context.Background(), metadata.MD(expect)) - - h := newHeaderReceiver(ctx, nil, true) - - for i := 0; i < 3; i++ { - cc, _, err := h.combineHeaders(ctx, nil) - - require.NoError(t, err) - requireContainsAll(t, client.FromContext(cc).Metadata, expect) - } -} - -func TestHeaderReceiverNoIncludeMetadata(t *testing.T) { - noExpect := map[string][]string{ - "K": {"k1", "k2"}, - "L": {"l1"}, - } - - ctx := metadata.NewIncomingContext(context.Background(), metadata.MD(noExpect)) - - h := newHeaderReceiver(ctx, nil, false) - - for i := 0; i < 3; i++ { - cc, _, err := h.combineHeaders(ctx, nil) - - require.NoError(t, err) - requireContainsNone(t, client.FromContext(cc).Metadata, noExpect) - } -} - -func TestHeaderReceiverAuthServerNoIncludeMetadata(t *testing.T) { - expectForAuth := map[string][]string{ - "L": {"k1", "k2"}, - "K": {"l1"}, - } - - ctx := metadata.NewIncomingContext(context.Background(), metadata.MD(expectForAuth)) - - ctrl := gomock.NewController(t) - as := mock.NewMockServer(ctrl) - - // The auth server is not called, it just needs to be non-nil. - as.EXPECT().Authenticate(gomock.Any(), gomock.Any()).Times(0) - - h := newHeaderReceiver(ctx, as, false) - - for i := 0; i < 3; i++ { - cc, hdrs, err := h.combineHeaders(ctx, nil) - - // The incoming metadata keys are not in the context. - require.NoError(t, err) - requireContainsNone(t, client.FromContext(cc).Metadata, expectForAuth) - - // Headers are returned for the auth server, though - // names have been forced to lower case. - require.Equal(t, len(hdrs), len(expectForAuth)) - for k, v := range expectForAuth { - require.Equal(t, hdrs[strings.ToLower(k)], v) - } - } -} - -func TestHeaderReceiverRequestNoStreamMetadata(t *testing.T) { - expect := map[string][]string{ - "K": {"k1", "k2"}, - "L": {"l1"}, - } - - var hpb bytes.Buffer - - hpe := hpack.NewEncoder(&hpb) - - ctx := context.Background() - - h := newHeaderReceiver(ctx, nil, true) - - for i := 0; i < 3; i++ { - hpb.Reset() - - for key, vals := range expect { - for _, val := range vals { - err := hpe.WriteField(hpack.HeaderField{ - Name: strings.ToLower(key), - Value: val, - }) - require.NoError(t, err) - } - } - - cc, _, err := h.combineHeaders(ctx, hpb.Bytes()) - - require.NoError(t, err) - requireContainsAll(t, client.FromContext(cc).Metadata, expect) - } -} - -func TestHeaderReceiverAuthServerIsSetNoIncludeMetadata(t *testing.T) { - expect := map[string][]string{ - "K": {"k1", "k2"}, - "L": {"l1"}, - } - - var hpb bytes.Buffer - - hpe := hpack.NewEncoder(&hpb) - - ctx := context.Background() - - ctrl := gomock.NewController(t) - as := mock.NewMockServer(ctrl) - - // The auth server is not called, it just needs to be non-nil. - as.EXPECT().Authenticate(gomock.Any(), gomock.Any()).Times(0) - - h := newHeaderReceiver(ctx, as, true) - - for i := 0; i < 3; i++ { - hpb.Reset() - - for key, vals := range expect { - for _, val := range vals { - err := hpe.WriteField(hpack.HeaderField{ - Name: strings.ToLower(key), - Value: val, - }) - require.NoError(t, err) - } - } - - cc, hdrs, err := h.combineHeaders(ctx, hpb.Bytes()) - - require.NoError(t, err) - - // Note: The call to client.Metadata.Get() inside - // requireContainsAll() actually modifies the metadata - // map (this is weird, but true and possibly - // valid). In cases where the input has incorrect - // case. It's not safe to check that the map sizes - // are equal after calling Get() below, so we assert - // same size first. - require.Equal(t, len(hdrs), len(expect)) - - requireContainsAll(t, client.FromContext(cc).Metadata, expect) - - // Headers passed to the auth server are equivalent w/ - // with names forced to lower case. - - for k, v := range expect { - require.Equal(t, hdrs[strings.ToLower(k)], v, "for %v", k) - } - } -} - -func TestHeaderReceiverBothMetadata(t *testing.T) { - expectK := map[string][]string{ - "K": {"k1", "k2"}, - } - expectL := map[string][]string{ - "L": {"l1"}, - "M": {"m1", "m2"}, - } - expect := map[string][]string{ - "K": {"k1", "k2"}, - "L": {"l1"}, - "M": {"m1", "m2"}, - } - - var hpb bytes.Buffer - - hpe := hpack.NewEncoder(&hpb) - - ctx := metadata.NewIncomingContext(context.Background(), metadata.MD(expectK)) - - h := newHeaderReceiver(ctx, nil, true) - - for i := 0; i < 3; i++ { - hpb.Reset() - - for key, vals := range expectL { - for _, val := range vals { - err := hpe.WriteField(hpack.HeaderField{ - Name: strings.ToLower(key), - Value: val, - }) - require.NoError(t, err) - } - } - - cc, _, err := h.combineHeaders(ctx, hpb.Bytes()) - - require.NoError(t, err) - requireContainsAll(t, client.FromContext(cc).Metadata, expect) - } -} - -func TestHeaderReceiverDuplicateMetadata(t *testing.T) { - expectStream := map[string][]string{ - "K": {"k1", "k2"}, - - // "M" value does not appear b/c the same header - // appears in per-request metadata. - "M": {""}, - } - expectRequest := map[string][]string{ - "L": {"l1"}, - "M": {"m1", "m2"}, - } - expectCombined := map[string][]string{ - "K": {"k1", "k2"}, - "L": {"l1"}, - "M": {"m1", "m2"}, - } - - var hpb bytes.Buffer - - hpe := hpack.NewEncoder(&hpb) - - ctx := metadata.NewIncomingContext(context.Background(), metadata.MD(expectStream)) - - h := newHeaderReceiver(ctx, nil, true) - - for i := 0; i < 3; i++ { - hpb.Reset() - - for key, vals := range expectRequest { - for _, val := range vals { - err := hpe.WriteField(hpack.HeaderField{ - Name: strings.ToLower(key), - Value: val, - }) - require.NoError(t, err) - } - } - - cc, _, err := h.combineHeaders(ctx, hpb.Bytes()) - - require.NoError(t, err) - requireContainsAll(t, client.FromContext(cc).Metadata, expectCombined) - } -} - -func TestReceiverAuthHeadersStream(t *testing.T) { - t.Run("no-metadata", func(t *testing.T) { testReceiverAuthHeaders(t, false, false) }) - t.Run("per-stream", func(t *testing.T) { testReceiverAuthHeaders(t, true, false) }) - t.Run("per-data", func(t *testing.T) { testReceiverAuthHeaders(t, true, true) }) -} - -func testReceiverAuthHeaders(t *testing.T, includeMeta bool, dataAuth bool) { - tc := healthyTestChannel{} - ctc := newCommonTestCase(t, tc) - - expectData := []map[string][]string{ - {"auth": []string{"true"}}, - nil, - {"auth": []string{"false"}}, - nil, - } - - var recvBatches []*arrowpb.BatchStatus - - ctc.stream.EXPECT().Send(gomock.Any()).Times(len(expectData)).DoAndReturn(func(batch *arrowpb.BatchStatus) error { - recvBatches = append(recvBatches, batch) - return nil - }) - - var authCall *gomock.Call - ctc.start(ctc.newRealConsumer, defaultBQ(), func(gsettings *configgrpc.ServerConfig, authPtr *auth.Server) { - gsettings.IncludeMetadata = includeMeta - - as := mock.NewMockServer(ctc.ctrl) - *authPtr = as - - authCall = as.EXPECT().Authenticate(gomock.Any(), gomock.Any()).AnyTimes() - }) - - dataCount := 0 - - authCall.DoAndReturn(func(ctx context.Context, hdrs map[string][]string) (context.Context, error) { - dataCount++ - if !dataAuth { - return ctx, nil - } - - ok := false - for _, val := range hdrs["auth"] { - ok = ok || (val == "true") - } - - if ok { - newmd := map[string][]string{} - for k, v := range hdrs { - newmd[k] = v - } - newmd["has_auth"] = []string{":+1:", ":100:"} - return client.NewContext(ctx, client.Info{ - Metadata: client.NewMetadata(newmd), - }), nil - } - return ctx, fmt.Errorf("not authorized") - }) - - go func() { - var hpb bytes.Buffer - hpe := hpack.NewEncoder(&hpb) - - for _, md := range expectData { - td := testdata.GenerateTraces(2) - - batch, err := ctc.testProducer.BatchArrowRecordsFromTraces(td) - require.NoError(t, err) - - batch = copyBatch(batch) - - if len(md) != 0 { - - hpb.Reset() - for key, vals := range md { - for _, val := range vals { - err := hpe.WriteField(hpack.HeaderField{ - Name: strings.ToLower(key), - Value: val, - }) - require.NoError(t, err) - } - } - - batch.Headers = make([]byte, hpb.Len()) - copy(batch.Headers, hpb.Bytes()) - } - ctc.putBatch(batch, nil) - } - close(ctc.receive) - }() - - var expectErrs []bool - - for _, testInput := range expectData { - // The static stream context contains one extra variable. - cpy := map[string][]string{} - cpy["stream_ctx"] = []string{"per-request"} - - for k, v := range testInput { - cpy[k] = v - } - - expectErr := false - if dataAuth { - hasAuth := false - for _, val := range cpy["auth"] { - hasAuth = hasAuth || (val == "true") - } - if hasAuth { - cpy["has_auth"] = []string{":+1:", ":100:"} - } else { - expectErr = true - } - } - - expectErrs = append(expectErrs, expectErr) - - if expectErr { - continue - } - - info := client.FromContext((<-ctc.consume).Ctx) - - for key, vals := range cpy { - if includeMeta { - require.Equal(t, vals, info.Metadata.Get(key)) - } else { - require.Equal(t, []string(nil), info.Metadata.Get(key)) - } - } - } - - err := ctc.wait() - // EOF is treated the same as Canceled - requireCanceledStatus(t, err) - - // Add in expectErrs for when receiver sees EOF, - // the status code will not be arrowpb.StatusCode_OK. - expectErrs = append(expectErrs, true) - - require.Equal(t, len(expectData), dataCount) - require.Equal(t, len(recvBatches), dataCount) - - for idx, batch := range recvBatches { - if expectErrs[idx] { - require.NotEqual(t, arrowpb.StatusCode_OK, batch.StatusCode) - } else { - require.Equal(t, arrowpb.StatusCode_OK, batch.StatusCode) - } - } -} - -func TestHeaderReceiverIsTraced(t *testing.T) { - streamHeaders := map[string][]string{ - "K": {"k1", "k2"}, - } - requestHeaders := map[string][]string{ - "L": {"l1"}, - "traceparent": {"00-00112233445566778899aabbccddeeff-0011223344556677-01"}, - } - expectCombined := map[string][]string{ - "K": {"k1", "k2"}, - "L": {"l1"}, - } - - var hpb bytes.Buffer - - otel.SetTextMapPropagator(propagation.TraceContext{}) - - hpe := hpack.NewEncoder(&hpb) - - ctx := metadata.NewIncomingContext(context.Background(), metadata.MD(streamHeaders)) - - h := newHeaderReceiver(ctx, nil, true) - - for i := 0; i < 3; i++ { - hpb.Reset() - - for key, vals := range requestHeaders { - for _, val := range vals { - err := hpe.WriteField(hpack.HeaderField{ - Name: strings.ToLower(key), - Value: val, - }) - require.NoError(t, err) - } - } - - newCtx, _, err := h.combineHeaders(ctx, hpb.Bytes()) - - require.NoError(t, err) - requireContainsAll(t, client.FromContext(newCtx).Metadata, expectCombined) - - // Check for hard-coded trace and span IDs from `traceparent` header above. - spanCtx := trace.SpanContextFromContext(newCtx) - require.Equal( - t, - trace.TraceID{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, - spanCtx.TraceID()) - require.Equal( - t, - trace.SpanID{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}, - spanCtx.SpanID()) - } -} diff --git a/collector/receiver/otelarrowreceiver/internal/arrow/mock/auth.go b/collector/receiver/otelarrowreceiver/internal/arrow/mock/auth.go deleted file mode 100644 index 9fa3c833..00000000 --- a/collector/receiver/otelarrowreceiver/internal/arrow/mock/auth.go +++ /dev/null @@ -1,84 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: go.opentelemetry.io/collector/extension/auth (interfaces: Server) -// -// Generated by this command: -// -// mockgen -package mock go.opentelemetry.io/collector/extension/auth Server -// - -// Package mock is a generated GoMock package. -package mock - -import ( - context "context" - reflect "reflect" - - component "go.opentelemetry.io/collector/component" - gomock "go.uber.org/mock/gomock" -) - -// MockServer is a mock of Server interface. -type MockServer struct { - ctrl *gomock.Controller - recorder *MockServerMockRecorder -} - -// MockServerMockRecorder is the mock recorder for MockServer. -type MockServerMockRecorder struct { - mock *MockServer -} - -// NewMockServer creates a new mock instance. -func NewMockServer(ctrl *gomock.Controller) *MockServer { - mock := &MockServer{ctrl: ctrl} - mock.recorder = &MockServerMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockServer) EXPECT() *MockServerMockRecorder { - return m.recorder -} - -// Authenticate mocks base method. -func (m *MockServer) Authenticate(arg0 context.Context, arg1 map[string][]string) (context.Context, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Authenticate", arg0, arg1) - ret0, _ := ret[0].(context.Context) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Authenticate indicates an expected call of Authenticate. -func (mr *MockServerMockRecorder) Authenticate(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Authenticate", reflect.TypeOf((*MockServer)(nil).Authenticate), arg0, arg1) -} - -// Shutdown mocks base method. -func (m *MockServer) Shutdown(arg0 context.Context) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Shutdown", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// Shutdown indicates an expected call of Shutdown. -func (mr *MockServerMockRecorder) Shutdown(arg0 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockServer)(nil).Shutdown), arg0) -} - -// Start mocks base method. -func (m *MockServer) Start(arg0 context.Context, arg1 component.Host) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Start", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Start indicates an expected call of Start. -func (mr *MockServerMockRecorder) Start(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockServer)(nil).Start), arg0, arg1) -} diff --git a/collector/receiver/otelarrowreceiver/internal/arrow/mock/consumer.go b/collector/receiver/otelarrowreceiver/internal/arrow/mock/consumer.go deleted file mode 100644 index 7654f091..00000000 --- a/collector/receiver/otelarrowreceiver/internal/arrow/mock/consumer.go +++ /dev/null @@ -1,174 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: go.opentelemetry.io/collector/consumer (interfaces: Traces,Metrics,Logs) -// -// Generated by this command: -// -// mockgen -package mock go.opentelemetry.io/collector/consumer Traces,Metrics,Logs -// - -// Package mock is a generated GoMock package. -package mock - -import ( - context "context" - reflect "reflect" - - consumer "go.opentelemetry.io/collector/consumer" - plog "go.opentelemetry.io/collector/pdata/plog" - pmetric "go.opentelemetry.io/collector/pdata/pmetric" - ptrace "go.opentelemetry.io/collector/pdata/ptrace" - gomock "go.uber.org/mock/gomock" -) - -// MockTraces is a mock of Traces interface. -type MockTraces struct { - ctrl *gomock.Controller - recorder *MockTracesMockRecorder -} - -// MockTracesMockRecorder is the mock recorder for MockTraces. -type MockTracesMockRecorder struct { - mock *MockTraces -} - -// NewMockTraces creates a new mock instance. -func NewMockTraces(ctrl *gomock.Controller) *MockTraces { - mock := &MockTraces{ctrl: ctrl} - mock.recorder = &MockTracesMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockTraces) EXPECT() *MockTracesMockRecorder { - return m.recorder -} - -// Capabilities mocks base method. -func (m *MockTraces) Capabilities() consumer.Capabilities { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Capabilities") - ret0, _ := ret[0].(consumer.Capabilities) - return ret0 -} - -// Capabilities indicates an expected call of Capabilities. -func (mr *MockTracesMockRecorder) Capabilities() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Capabilities", reflect.TypeOf((*MockTraces)(nil).Capabilities)) -} - -// ConsumeTraces mocks base method. -func (m *MockTraces) ConsumeTraces(arg0 context.Context, arg1 ptrace.Traces) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConsumeTraces", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// ConsumeTraces indicates an expected call of ConsumeTraces. -func (mr *MockTracesMockRecorder) ConsumeTraces(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConsumeTraces", reflect.TypeOf((*MockTraces)(nil).ConsumeTraces), arg0, arg1) -} - -// MockMetrics is a mock of Metrics interface. -type MockMetrics struct { - ctrl *gomock.Controller - recorder *MockMetricsMockRecorder -} - -// MockMetricsMockRecorder is the mock recorder for MockMetrics. -type MockMetricsMockRecorder struct { - mock *MockMetrics -} - -// NewMockMetrics creates a new mock instance. -func NewMockMetrics(ctrl *gomock.Controller) *MockMetrics { - mock := &MockMetrics{ctrl: ctrl} - mock.recorder = &MockMetricsMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockMetrics) EXPECT() *MockMetricsMockRecorder { - return m.recorder -} - -// Capabilities mocks base method. -func (m *MockMetrics) Capabilities() consumer.Capabilities { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Capabilities") - ret0, _ := ret[0].(consumer.Capabilities) - return ret0 -} - -// Capabilities indicates an expected call of Capabilities. -func (mr *MockMetricsMockRecorder) Capabilities() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Capabilities", reflect.TypeOf((*MockMetrics)(nil).Capabilities)) -} - -// ConsumeMetrics mocks base method. -func (m *MockMetrics) ConsumeMetrics(arg0 context.Context, arg1 pmetric.Metrics) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConsumeMetrics", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// ConsumeMetrics indicates an expected call of ConsumeMetrics. -func (mr *MockMetricsMockRecorder) ConsumeMetrics(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConsumeMetrics", reflect.TypeOf((*MockMetrics)(nil).ConsumeMetrics), arg0, arg1) -} - -// MockLogs is a mock of Logs interface. -type MockLogs struct { - ctrl *gomock.Controller - recorder *MockLogsMockRecorder -} - -// MockLogsMockRecorder is the mock recorder for MockLogs. -type MockLogsMockRecorder struct { - mock *MockLogs -} - -// NewMockLogs creates a new mock instance. -func NewMockLogs(ctrl *gomock.Controller) *MockLogs { - mock := &MockLogs{ctrl: ctrl} - mock.recorder = &MockLogsMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockLogs) EXPECT() *MockLogsMockRecorder { - return m.recorder -} - -// Capabilities mocks base method. -func (m *MockLogs) Capabilities() consumer.Capabilities { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Capabilities") - ret0, _ := ret[0].(consumer.Capabilities) - return ret0 -} - -// Capabilities indicates an expected call of Capabilities. -func (mr *MockLogsMockRecorder) Capabilities() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Capabilities", reflect.TypeOf((*MockLogs)(nil).Capabilities)) -} - -// ConsumeLogs mocks base method. -func (m *MockLogs) ConsumeLogs(arg0 context.Context, arg1 plog.Logs) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConsumeLogs", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// ConsumeLogs indicates an expected call of ConsumeLogs. -func (mr *MockLogsMockRecorder) ConsumeLogs(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConsumeLogs", reflect.TypeOf((*MockLogs)(nil).ConsumeLogs), arg0, arg1) -} diff --git a/collector/receiver/otelarrowreceiver/internal/logs/otlp.go b/collector/receiver/otelarrowreceiver/internal/logs/otlp.go deleted file mode 100644 index b6bd2f74..00000000 --- a/collector/receiver/otelarrowreceiver/internal/logs/otlp.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package logs // import "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/logs" - -import ( - "context" - - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/pdata/plog/plogotlp" - "go.opentelemetry.io/collector/receiver/receiverhelper" -) - -const dataFormatProtobuf = "protobuf" - -// Receiver is the type used to handle logs from OpenTelemetry exporters. -type Receiver struct { - plogotlp.UnimplementedGRPCServer - nextConsumer consumer.Logs - obsrecv *receiverhelper.ObsReport -} - -// New creates a new Receiver reference. -func New(nextConsumer consumer.Logs, obsrecv *receiverhelper.ObsReport) *Receiver { - return &Receiver{ - nextConsumer: nextConsumer, - obsrecv: obsrecv, - } -} - -// Export implements the service Export logs func. -func (r *Receiver) Export(ctx context.Context, req plogotlp.ExportRequest) (plogotlp.ExportResponse, error) { - ld := req.Logs() - numSpans := ld.LogRecordCount() - if numSpans == 0 { - return plogotlp.NewExportResponse(), nil - } - - ctx = r.obsrecv.StartLogsOp(ctx) - err := r.nextConsumer.ConsumeLogs(ctx, ld) - r.obsrecv.EndLogsOp(ctx, dataFormatProtobuf, numSpans, err) - - return plogotlp.NewExportResponse(), err -} - -func (r *Receiver) Consumer() consumer.Logs { - return r.nextConsumer -} diff --git a/collector/receiver/otelarrowreceiver/internal/logs/otlp_test.go b/collector/receiver/otelarrowreceiver/internal/logs/otlp_test.go deleted file mode 100644 index 618beb28..00000000 --- a/collector/receiver/otelarrowreceiver/internal/logs/otlp_test.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package logs - -import ( - "context" - "errors" - "net" - "testing" - - "github.com/open-telemetry/otel-arrow/collector/testdata" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/consumer/consumertest" - "go.opentelemetry.io/collector/pdata/plog/plogotlp" - "go.opentelemetry.io/collector/receiver/receiverhelper" - "go.opentelemetry.io/collector/receiver/receivertest" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -func TestExport(t *testing.T) { - ld := testdata.GenerateLogs(1) - req := plogotlp.NewExportRequestFromLogs(ld) - - logSink := new(consumertest.LogsSink) - logClient := makeLogsServiceClient(t, logSink) - resp, err := logClient.Export(context.Background(), req) - require.NoError(t, err, "Failed to export trace: %v", err) - require.NotNil(t, resp, "The response is missing") - - lds := logSink.AllLogs() - require.Len(t, lds, 1) - assert.EqualValues(t, ld, lds[0]) -} - -func TestExport_EmptyRequest(t *testing.T) { - logSink := new(consumertest.LogsSink) - - logClient := makeLogsServiceClient(t, logSink) - resp, err := logClient.Export(context.Background(), plogotlp.NewExportRequest()) - assert.NoError(t, err, "Failed to export trace: %v", err) - assert.NotNil(t, resp, "The response is missing") -} - -func TestExport_ErrorConsumer(t *testing.T) { - ld := testdata.GenerateLogs(1) - req := plogotlp.NewExportRequestFromLogs(ld) - - logClient := makeLogsServiceClient(t, consumertest.NewErr(errors.New("my error"))) - resp, err := logClient.Export(context.Background(), req) - assert.EqualError(t, err, "rpc error: code = Unknown desc = my error") - assert.Equal(t, plogotlp.ExportResponse{}, resp) -} - -func makeLogsServiceClient(t *testing.T, lc consumer.Logs) plogotlp.GRPCClient { - addr := otlpReceiverOnGRPCServer(t, lc) - cc, err := grpc.NewClient(addr.String(), grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err, "Failed to create the TraceServiceClient: %v", err) - t.Cleanup(func() { - require.NoError(t, cc.Close()) - }) - - return plogotlp.NewGRPCClient(cc) -} - -func otlpReceiverOnGRPCServer(t *testing.T, lc consumer.Logs) net.Addr { - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - - t.Cleanup(func() { - require.NoError(t, ln.Close()) - }) - - set := receivertest.NewNopSettings() - set.ID = component.NewIDWithName(component.MustNewType("otlp"), "log") - obsrecv, err := receiverhelper.NewObsReport(receiverhelper.ObsReportSettings{ - ReceiverID: set.ID, - Transport: "grpc", - ReceiverCreateSettings: set, - }) - require.NoError(t, err) - r := New(lc, obsrecv) - // Now run it as a gRPC server - srv := grpc.NewServer() - plogotlp.RegisterGRPCServer(srv, r) - go func() { - _ = srv.Serve(ln) - }() - - return ln.Addr() -} diff --git a/collector/receiver/otelarrowreceiver/internal/metadata/generated_status.go b/collector/receiver/otelarrowreceiver/internal/metadata/generated_status.go deleted file mode 100644 index 62851635..00000000 --- a/collector/receiver/otelarrowreceiver/internal/metadata/generated_status.go +++ /dev/null @@ -1,17 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package metadata - -import ( - "go.opentelemetry.io/collector/component" -) - -var ( - Type = component.MustNewType("otelarrow") -) - -const ( - MetricsStability = component.StabilityLevelDevelopment - TracesStability = component.StabilityLevelDevelopment - LogsStability = component.StabilityLevelDevelopment -) diff --git a/collector/receiver/otelarrowreceiver/internal/metadata/generated_telemetry.go b/collector/receiver/otelarrowreceiver/internal/metadata/generated_telemetry.go deleted file mode 100644 index f8f70fb8..00000000 --- a/collector/receiver/otelarrowreceiver/internal/metadata/generated_telemetry.go +++ /dev/null @@ -1,17 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package metadata - -import ( - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/trace" -) - -func Meter(settings component.TelemetrySettings) metric.Meter { - return settings.MeterProvider.Meter("otelcol/otelarrowreceiver") -} - -func Tracer(settings component.TelemetrySettings) trace.Tracer { - return settings.TracerProvider.Tracer("otelcol/otelarrowreceiver") -} diff --git a/collector/receiver/otelarrowreceiver/internal/metadata/generated_telemetry_test.go b/collector/receiver/otelarrowreceiver/internal/metadata/generated_telemetry_test.go deleted file mode 100644 index 3d366b73..00000000 --- a/collector/receiver/otelarrowreceiver/internal/metadata/generated_telemetry_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Code generated by mdatagen. DO NOT EDIT. - -package metadata - -import ( - "testing" - - "github.com/stretchr/testify/require" - "go.opentelemetry.io/otel/metric" - embeddedmetric "go.opentelemetry.io/otel/metric/embedded" - noopmetric "go.opentelemetry.io/otel/metric/noop" - "go.opentelemetry.io/otel/trace" - embeddedtrace "go.opentelemetry.io/otel/trace/embedded" - nooptrace "go.opentelemetry.io/otel/trace/noop" - - "go.opentelemetry.io/collector/component" -) - -type mockMeter struct { - noopmetric.Meter - name string -} -type mockMeterProvider struct { - embeddedmetric.MeterProvider -} - -func (m mockMeterProvider) Meter(name string, opts ...metric.MeterOption) metric.Meter { - return mockMeter{name: name} -} - -type mockTracer struct { - nooptrace.Tracer - name string -} - -type mockTracerProvider struct { - embeddedtrace.TracerProvider -} - -func (m mockTracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer { - return mockTracer{name: name} -} - -func TestProviders(t *testing.T) { - set := component.TelemetrySettings{ - MeterProvider: mockMeterProvider{}, - TracerProvider: mockTracerProvider{}, - } - - meter := Meter(set) - if m, ok := meter.(mockMeter); ok { - require.Equal(t, "otelcol/otelarrowreceiver", m.name) - } else { - require.Fail(t, "returned Meter not mockMeter") - } - - tracer := Tracer(set) - if m, ok := tracer.(mockTracer); ok { - require.Equal(t, "otelcol/otelarrowreceiver", m.name) - } else { - require.Fail(t, "returned Meter not mockTracer") - } -} diff --git a/collector/receiver/otelarrowreceiver/internal/metrics/otlp.go b/collector/receiver/otelarrowreceiver/internal/metrics/otlp.go deleted file mode 100644 index 70420603..00000000 --- a/collector/receiver/otelarrowreceiver/internal/metrics/otlp.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package metrics // import "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/metrics" - -import ( - "context" - - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" - "go.opentelemetry.io/collector/receiver/receiverhelper" -) - -const dataFormatProtobuf = "protobuf" - -// Receiver is the type used to handle metrics from OpenTelemetry exporters. -type Receiver struct { - pmetricotlp.UnimplementedGRPCServer - nextConsumer consumer.Metrics - obsrecv *receiverhelper.ObsReport -} - -// New creates a new Receiver reference. -func New(nextConsumer consumer.Metrics, obsrecv *receiverhelper.ObsReport) *Receiver { - return &Receiver{ - nextConsumer: nextConsumer, - obsrecv: obsrecv, - } -} - -// Export implements the service Export metrics func. -func (r *Receiver) Export(ctx context.Context, req pmetricotlp.ExportRequest) (pmetricotlp.ExportResponse, error) { - md := req.Metrics() - dataPointCount := md.DataPointCount() - if dataPointCount == 0 { - return pmetricotlp.NewExportResponse(), nil - } - - ctx = r.obsrecv.StartMetricsOp(ctx) - err := r.nextConsumer.ConsumeMetrics(ctx, md) - r.obsrecv.EndMetricsOp(ctx, dataFormatProtobuf, dataPointCount, err) - - return pmetricotlp.NewExportResponse(), err -} - -func (r *Receiver) Consumer() consumer.Metrics { - return r.nextConsumer -} diff --git a/collector/receiver/otelarrowreceiver/internal/metrics/otlp_test.go b/collector/receiver/otelarrowreceiver/internal/metrics/otlp_test.go deleted file mode 100644 index 22a18897..00000000 --- a/collector/receiver/otelarrowreceiver/internal/metrics/otlp_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package metrics - -import ( - "context" - "errors" - "net" - "testing" - - "github.com/open-telemetry/otel-arrow/collector/testdata" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/consumer/consumertest" - "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" - "go.opentelemetry.io/collector/receiver/receiverhelper" - "go.opentelemetry.io/collector/receiver/receivertest" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -func TestExport(t *testing.T) { - md := testdata.GenerateMetrics(1) - req := pmetricotlp.NewExportRequestFromMetrics(md) - - metricSink := new(consumertest.MetricsSink) - metricsClient := makeMetricsServiceClient(t, metricSink) - resp, err := metricsClient.Export(context.Background(), req) - - require.NoError(t, err, "Failed to export metrics: %v", err) - require.NotNil(t, resp, "The response is missing") - - mds := metricSink.AllMetrics() - require.Len(t, mds, 1) - assert.EqualValues(t, md, mds[0]) -} - -func TestExport_EmptyRequest(t *testing.T) { - metricSink := new(consumertest.MetricsSink) - metricsClient := makeMetricsServiceClient(t, metricSink) - resp, err := metricsClient.Export(context.Background(), pmetricotlp.NewExportRequest()) - require.NoError(t, err) - require.NotNil(t, resp) -} - -func TestExport_ErrorConsumer(t *testing.T) { - md := testdata.GenerateMetrics(1) - req := pmetricotlp.NewExportRequestFromMetrics(md) - - metricsClient := makeMetricsServiceClient(t, consumertest.NewErr(errors.New("my error"))) - resp, err := metricsClient.Export(context.Background(), req) - assert.EqualError(t, err, "rpc error: code = Unknown desc = my error") - assert.Equal(t, pmetricotlp.ExportResponse{}, resp) -} - -func makeMetricsServiceClient(t *testing.T, mc consumer.Metrics) pmetricotlp.GRPCClient { - addr := otlpReceiverOnGRPCServer(t, mc) - - cc, err := grpc.NewClient(addr.String(), grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err, "Failed to create the MetricsServiceClient: %v", err) - t.Cleanup(func() { - require.NoError(t, cc.Close()) - }) - - return pmetricotlp.NewGRPCClient(cc) -} - -func otlpReceiverOnGRPCServer(t *testing.T, mc consumer.Metrics) net.Addr { - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - - t.Cleanup(func() { - require.NoError(t, ln.Close()) - }) - - set := receivertest.NewNopSettings() - set.ID = component.NewIDWithName(component.MustNewType("otlp"), "metrics") - obsrecv, err := receiverhelper.NewObsReport(receiverhelper.ObsReportSettings{ - ReceiverID: set.ID, - Transport: "grpc", - ReceiverCreateSettings: set, - }) - require.NoError(t, err) - r := New(mc, obsrecv) - // Now run it as a gRPC server - srv := grpc.NewServer() - pmetricotlp.RegisterGRPCServer(srv, r) - go func() { - _ = srv.Serve(ln) - }() - - return ln.Addr() -} diff --git a/collector/receiver/otelarrowreceiver/internal/trace/otlp.go b/collector/receiver/otelarrowreceiver/internal/trace/otlp.go deleted file mode 100644 index eb3f90df..00000000 --- a/collector/receiver/otelarrowreceiver/internal/trace/otlp.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package trace // import "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/trace" - -import ( - "context" - - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" - "go.opentelemetry.io/collector/receiver/receiverhelper" -) - -const dataFormatProtobuf = "protobuf" - -// Receiver is the type used to handle spans from OpenTelemetry exporters. -type Receiver struct { - ptraceotlp.UnimplementedGRPCServer - nextConsumer consumer.Traces - obsrecv *receiverhelper.ObsReport -} - -// New creates a new Receiver reference. -func New(nextConsumer consumer.Traces, obsrecv *receiverhelper.ObsReport) *Receiver { - return &Receiver{ - nextConsumer: nextConsumer, - obsrecv: obsrecv, - } -} - -// Export implements the service Export traces func. -func (r *Receiver) Export(ctx context.Context, req ptraceotlp.ExportRequest) (ptraceotlp.ExportResponse, error) { - td := req.Traces() - // We need to ensure that it propagates the receiver name as a tag - numSpans := td.SpanCount() - if numSpans == 0 { - return ptraceotlp.NewExportResponse(), nil - } - - ctx = r.obsrecv.StartTracesOp(ctx) - err := r.nextConsumer.ConsumeTraces(ctx, td) - r.obsrecv.EndTracesOp(ctx, dataFormatProtobuf, numSpans, err) - - return ptraceotlp.NewExportResponse(), err -} - -func (r *Receiver) Consumer() consumer.Traces { - return r.nextConsumer -} diff --git a/collector/receiver/otelarrowreceiver/internal/trace/otlp_test.go b/collector/receiver/otelarrowreceiver/internal/trace/otlp_test.go deleted file mode 100644 index e23c2bae..00000000 --- a/collector/receiver/otelarrowreceiver/internal/trace/otlp_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package trace - -import ( - "context" - "errors" - "net" - "testing" - - "github.com/open-telemetry/otel-arrow/collector/testdata" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/consumer/consumertest" - "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" - "go.opentelemetry.io/collector/receiver/receiverhelper" - "go.opentelemetry.io/collector/receiver/receivertest" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -func TestExport(t *testing.T) { - td := testdata.GenerateTraces(1) - req := ptraceotlp.NewExportRequestFromTraces(td) - - traceSink := new(consumertest.TracesSink) - traceClient := makeTraceServiceClient(t, traceSink) - resp, err := traceClient.Export(context.Background(), req) - require.NoError(t, err, "Failed to export trace: %v", err) - require.NotNil(t, resp, "The response is missing") - - require.Len(t, traceSink.AllTraces(), 1) - assert.EqualValues(t, td, traceSink.AllTraces()[0]) -} - -func TestExport_EmptyRequest(t *testing.T) { - traceSink := new(consumertest.TracesSink) - traceClient := makeTraceServiceClient(t, traceSink) - resp, err := traceClient.Export(context.Background(), ptraceotlp.NewExportRequest()) - assert.NoError(t, err, "Failed to export trace: %v", err) - assert.NotNil(t, resp, "The response is missing") -} - -func TestExport_ErrorConsumer(t *testing.T) { - td := testdata.GenerateTraces(1) - req := ptraceotlp.NewExportRequestFromTraces(td) - - traceClient := makeTraceServiceClient(t, consumertest.NewErr(errors.New("my error"))) - resp, err := traceClient.Export(context.Background(), req) - assert.EqualError(t, err, "rpc error: code = Unknown desc = my error") - assert.Equal(t, ptraceotlp.ExportResponse{}, resp) -} - -func makeTraceServiceClient(t *testing.T, tc consumer.Traces) ptraceotlp.GRPCClient { - addr := otlpReceiverOnGRPCServer(t, tc) - cc, err := grpc.NewClient(addr.String(), grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err, "Failed to create the TraceServiceClient: %v", err) - t.Cleanup(func() { - require.NoError(t, cc.Close()) - }) - - return ptraceotlp.NewGRPCClient(cc) -} - -func otlpReceiverOnGRPCServer(t *testing.T, tc consumer.Traces) net.Addr { - ln, err := net.Listen("tcp", "localhost:") - require.NoError(t, err, "Failed to find an available address to run the gRPC server: %v", err) - - t.Cleanup(func() { - require.NoError(t, ln.Close()) - }) - - set := receivertest.NewNopSettings() - set.ID = component.NewIDWithName(component.MustNewType("otlp"), "trace") - obsrecv, err := receiverhelper.NewObsReport(receiverhelper.ObsReportSettings{ - ReceiverID: set.ID, - Transport: "grpc", - ReceiverCreateSettings: set, - }) - require.NoError(t, err) - r := New(tc, obsrecv) - // Now run it as a gRPC server - srv := grpc.NewServer() - ptraceotlp.RegisterGRPCServer(srv, r) - go func() { - _ = srv.Serve(ln) - }() - - return ln.Addr() -} diff --git a/collector/receiver/otelarrowreceiver/metadata.yaml b/collector/receiver/otelarrowreceiver/metadata.yaml deleted file mode 100644 index 914ed40e..00000000 --- a/collector/receiver/otelarrowreceiver/metadata.yaml +++ /dev/null @@ -1,7 +0,0 @@ -type: otelarrow - -status: - class: receiver - stability: - development: [metrics, traces, logs] - distributions: [otelarrow] diff --git a/collector/receiver/otelarrowreceiver/otelarrow.go b/collector/receiver/otelarrowreceiver/otelarrow.go deleted file mode 100644 index 6e4f1b34..00000000 --- a/collector/receiver/otelarrowreceiver/otelarrow.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowreceiver // import "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver" - -import ( - "context" - "errors" - "sync" - - arrowpb "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1" - "github.com/open-telemetry/otel-arrow/collector/compression/zstd" - "github.com/open-telemetry/otel-arrow/collector/netstats" - arrowRecord "github.com/open-telemetry/otel-arrow/pkg/otel/arrow_record" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/extension/auth" - "go.opentelemetry.io/collector/pdata/plog/plogotlp" - "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" - "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" - "go.opentelemetry.io/collector/receiver" - "go.opentelemetry.io/collector/receiver/receiverhelper" - "go.uber.org/zap" - "google.golang.org/grpc" - - "github.com/open-telemetry/otel-arrow/collector/admission" - "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/arrow" - "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/logs" - "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/metrics" - "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/trace" -) - -// otelArrowReceiver is the type that exposes Trace and Metrics reception. -type otelArrowReceiver struct { - cfg *Config - serverGRPC *grpc.Server - - tracesReceiver *trace.Receiver - metricsReceiver *metrics.Receiver - logsReceiver *logs.Receiver - arrowReceiver *arrow.Receiver - shutdownWG sync.WaitGroup - - obsrepGRPC *receiverhelper.ObsReport - netReporter *netstats.NetworkReporter - - settings receiver.Settings -} - -// newOTelArrowReceiver just creates the OpenTelemetry receiver services. It is the caller's -// responsibility to invoke the respective Start*Reception methods as well -// as the various Stop*Reception methods to end it. -func newOTelArrowReceiver(cfg *Config, set receiver.Settings) (*otelArrowReceiver, error) { - netReporter, err := netstats.NewReceiverNetworkReporter(set) - if err != nil { - return nil, err - } - r := &otelArrowReceiver{ - cfg: cfg, - settings: set, - netReporter: netReporter, - } - if err = zstd.SetDecoderConfig(cfg.Arrow.Zstd); err != nil { - return nil, err - } - - r.obsrepGRPC, err = receiverhelper.NewObsReport(receiverhelper.ObsReportSettings{ - ReceiverID: set.ID, - Transport: "grpc", - ReceiverCreateSettings: set, - }) - if err != nil { - return nil, err - } - - return r, nil -} - -func (r *otelArrowReceiver) startGRPCServer(cfg configgrpc.ServerConfig, _ component.Host) error { - r.settings.Logger.Info("Starting GRPC server", zap.String("endpoint", cfg.NetAddr.Endpoint)) - - gln, err := cfg.NetAddr.Listen(context.Background()) - if err != nil { - return err - } - r.shutdownWG.Add(1) - go func() { - defer r.shutdownWG.Done() - - if errGrpc := r.serverGRPC.Serve(gln); errGrpc != nil && !errors.Is(errGrpc, grpc.ErrServerStopped) { - r.settings.ReportStatus(component.NewFatalErrorEvent(errGrpc)) - } - }() - return nil -} - -func (r *otelArrowReceiver) startProtocolServers(ctx context.Context, host component.Host) error { - var err error - var serverOpts []grpc.ServerOption - - if r.netReporter != nil { - serverOpts = append(serverOpts, grpc.StatsHandler(r.netReporter.Handler())) - } - r.serverGRPC, err = r.cfg.GRPC.ToServer(ctx, host, r.settings.TelemetrySettings, serverOpts...) - if err != nil { - return err - } - - var authServer auth.Server - if r.cfg.GRPC.Auth != nil { - authServer, err = r.cfg.GRPC.Auth.GetServerAuthenticatorContext(ctx, host.GetExtensions()) - if err != nil { - return err - } - } - bq := admission.NewBoundedQueue(int64(r.cfg.Arrow.AdmissionLimitMiB<<20), r.cfg.Arrow.WaiterLimit) - - r.arrowReceiver, err = arrow.New(arrow.Consumers(r), r.settings, r.obsrepGRPC, r.cfg.GRPC, authServer, func() arrowRecord.ConsumerAPI { - var opts []arrowRecord.Option - if r.cfg.Arrow.MemoryLimitMiB != 0 { - // in which case the default is selected in the arrowRecord package. - opts = append(opts, arrowRecord.WithMemoryLimit(r.cfg.Arrow.MemoryLimitMiB<<20)) - } - if r.settings.TelemetrySettings.MeterProvider != nil { - opts = append(opts, arrowRecord.WithMeterProvider(r.settings.TelemetrySettings.MeterProvider, r.settings.TelemetrySettings.MetricsLevel)) - } - return arrowRecord.NewConsumer(opts...) - }, bq, r.netReporter) - - if err != nil { - return err - } - - if r.tracesReceiver != nil { - ptraceotlp.RegisterGRPCServer(r.serverGRPC, r.tracesReceiver) - - arrowpb.RegisterArrowTracesServiceServer(r.serverGRPC, r.arrowReceiver) - } - - if r.metricsReceiver != nil { - pmetricotlp.RegisterGRPCServer(r.serverGRPC, r.metricsReceiver) - - arrowpb.RegisterArrowMetricsServiceServer(r.serverGRPC, r.arrowReceiver) - } - - if r.logsReceiver != nil { - plogotlp.RegisterGRPCServer(r.serverGRPC, r.logsReceiver) - - arrowpb.RegisterArrowLogsServiceServer(r.serverGRPC, r.arrowReceiver) - } - - err = r.startGRPCServer(r.cfg.GRPC, host) - if err != nil { - return err - } - - return err -} - -// Start runs the trace receiver on the gRPC server. Currently -// it also enables the metrics receiver too. -func (r *otelArrowReceiver) Start(ctx context.Context, host component.Host) error { - return r.startProtocolServers(ctx, host) -} - -// Shutdown is a method to turn off receiving. -func (r *otelArrowReceiver) Shutdown(_ context.Context) error { - var err error - - if r.serverGRPC != nil { - r.serverGRPC.GracefulStop() - } - - r.shutdownWG.Wait() - return err -} - -func (r *otelArrowReceiver) registerTraceConsumer(tc consumer.Traces) { - r.tracesReceiver = trace.New(tc, r.obsrepGRPC) -} - -func (r *otelArrowReceiver) registerMetricsConsumer(mc consumer.Metrics) { - r.metricsReceiver = metrics.New(mc, r.obsrepGRPC) -} - -func (r *otelArrowReceiver) registerLogsConsumer(lc consumer.Logs) { - r.logsReceiver = logs.New(lc, r.obsrepGRPC) -} - -var _ arrow.Consumers = &otelArrowReceiver{} - -func (r *otelArrowReceiver) Traces() consumer.Traces { - if r.tracesReceiver == nil { - return nil - } - return r.tracesReceiver.Consumer() -} - -func (r *otelArrowReceiver) Metrics() consumer.Metrics { - if r.metricsReceiver == nil { - return nil - } - return r.metricsReceiver.Consumer() -} - -func (r *otelArrowReceiver) Logs() consumer.Logs { - if r.logsReceiver == nil { - return nil - } - return r.logsReceiver.Consumer() -} diff --git a/collector/receiver/otelarrowreceiver/otelarrow_test.go b/collector/receiver/otelarrowreceiver/otelarrow_test.go deleted file mode 100644 index 34c97940..00000000 --- a/collector/receiver/otelarrowreceiver/otelarrow_test.go +++ /dev/null @@ -1,676 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelarrowreceiver - -import ( - "bytes" - "context" - "errors" - "fmt" - "net" - "strconv" - "sync" - "testing" - "time" - - arrowpb "github.com/open-telemetry/otel-arrow/api/experimental/arrow/v1" - "github.com/open-telemetry/otel-arrow/collector/testdata" - "github.com/open-telemetry/otel-arrow/collector/testutil" - arrowRecord "github.com/open-telemetry/otel-arrow/pkg/otel/arrow_record" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/client" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/component/componenttest" - "go.opentelemetry.io/collector/config/configauth" - "go.opentelemetry.io/collector/config/configgrpc" - "go.opentelemetry.io/collector/config/confignet" - "go.opentelemetry.io/collector/config/configtelemetry" - "go.opentelemetry.io/collector/config/configtls" - "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/consumer/consumertest" - "go.opentelemetry.io/collector/extension/auth" - "go.opentelemetry.io/collector/pdata/pmetric" - "go.opentelemetry.io/collector/pdata/ptrace" - "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" - "go.opentelemetry.io/collector/receiver" - "go.opentelemetry.io/collector/receiver/receivertest" - "go.uber.org/mock/gomock" - "golang.org/x/net/http2/hpack" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - - "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/arrow/mock" - componentMetadata "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver/internal/metadata" -) - -const otlpReceiverName = "receiver_test" - -var testReceiverID = component.NewIDWithName(componentMetadata.Type, otlpReceiverName) - -func TestGRPCNewPortAlreadyUsed(t *testing.T) { - addr := testutil.GetAvailableLocalAddress(t) - ln, err := net.Listen("tcp", addr) - require.NoError(t, err, "failed to listen on %q: %v", addr, err) - t.Cleanup(func() { - assert.NoError(t, ln.Close()) - }) - tt := componenttest.NewNopTelemetrySettings() - r := newGRPCReceiver(t, addr, tt, consumertest.NewNop(), consumertest.NewNop()) - require.NotNil(t, r) - - require.Error(t, r.Start(context.Background(), componenttest.NewNopHost())) -} - -// TestOTelArrowReceiverGRPCTracesIngestTest checks that the gRPC trace receiver -// is returning the proper response (return and metrics) when the next consumer -// in the pipeline reports error. The test changes the responses returned by the -// next trace consumer, checks if data was passed down the pipeline and if -// proper metrics were recorded. It also uses all endpoints supported by the -// trace receiver. -func TestOTelArrowReceiverGRPCTracesIngestTest(t *testing.T) { - type ingestionStateTest struct { - okToIngest bool - expectedCode codes.Code - } - - expectedReceivedBatches := 2 - ingestionStates := []ingestionStateTest{ - { - okToIngest: true, - expectedCode: codes.OK, - }, - { - okToIngest: false, - expectedCode: codes.Unknown, - }, - { - okToIngest: true, - expectedCode: codes.OK, - }, - } - - addr := testutil.GetAvailableLocalAddress(t) - td := testdata.GenerateTraces(1) - - tt, err := componenttest.SetupTelemetry(testReceiverID) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) }) - - sink := &errOrSinkConsumer{TracesSink: new(consumertest.TracesSink)} - - ocr := newGRPCReceiver(t, addr, tt.TelemetrySettings(), sink, nil) - require.NotNil(t, ocr) - require.NoError(t, ocr.Start(context.Background(), componenttest.NewNopHost())) - t.Cleanup(func() { require.NoError(t, ocr.Shutdown(context.Background())) }) - - cc, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - defer func() { - assert.NoError(t, cc.Close()) - }() - - for _, ingestionState := range ingestionStates { - if ingestionState.okToIngest { - sink.SetConsumeError(nil) - } else { - sink.SetConsumeError(errors.New("consumer error")) - } - - _, err = ptraceotlp.NewGRPCClient(cc).Export(context.Background(), ptraceotlp.NewExportRequestFromTraces(td)) - errStatus, ok := status.FromError(err) - require.True(t, ok) - assert.Equal(t, ingestionState.expectedCode, errStatus.Code()) - } - - require.Equal(t, expectedReceivedBatches, len(sink.AllTraces())) - - expectedIngestionBlockedRPCs := 1 - require.NoError(t, tt.CheckReceiverTraces("grpc", int64(expectedReceivedBatches), int64(expectedIngestionBlockedRPCs))) -} - -func TestGRPCInvalidTLSCredentials(t *testing.T) { - cfg := &Config{ - Protocols: Protocols{ - GRPC: configgrpc.ServerConfig{ - NetAddr: confignet.AddrConfig{ - Endpoint: testutil.GetAvailableLocalAddress(t), - Transport: confignet.TransportTypeTCP, - }, - TLSSetting: &configtls.ServerConfig{ - Config: configtls.Config{ - CertFile: "willfail", - }, - }, - }, - }, - } - - r, err := NewFactory().CreateTracesReceiver( - context.Background(), - receivertest.NewNopSettings(), - cfg, - consumertest.NewNop()) - - require.NoError(t, err) - assert.NotNil(t, r) - - assert.EqualError(t, - r.Start(context.Background(), componenttest.NewNopHost()), - `failed to load TLS config: failed to load TLS cert and key: for auth via TLS, provide both certificate and key, or neither`) -} - -func TestGRPCMaxRecvSize(t *testing.T) { - addr := testutil.GetAvailableLocalAddress(t) - sink := new(consumertest.TracesSink) - - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.GRPC.NetAddr.Endpoint = addr - tt := componenttest.NewNopTelemetrySettings() - ocr := newReceiver(t, factory, tt, cfg, testReceiverID, sink, nil) - - require.NotNil(t, ocr) - require.NoError(t, ocr.Start(context.Background(), componenttest.NewNopHost())) - - cc, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - - td := testdata.GenerateTraces(50000) - require.Error(t, exportTraces(cc, td)) - assert.NoError(t, cc.Close()) - require.NoError(t, ocr.Shutdown(context.Background())) - - cfg.GRPC.MaxRecvMsgSizeMiB = 100 - - ocr = newReceiver(t, factory, tt, cfg, testReceiverID, sink, nil) - - require.NotNil(t, ocr) - require.NoError(t, ocr.Start(context.Background(), componenttest.NewNopHost())) - t.Cleanup(func() { require.NoError(t, ocr.Shutdown(context.Background())) }) - - cc, err = grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - defer func() { - assert.NoError(t, cc.Close()) - }() - - td = testdata.GenerateTraces(50000) - require.NoError(t, exportTraces(cc, td)) - require.Len(t, sink.AllTraces(), 1) - assert.Equal(t, td, sink.AllTraces()[0]) -} - -func newGRPCReceiver(t *testing.T, endpoint string, settings component.TelemetrySettings, tc consumer.Traces, mc consumer.Metrics) component.Component { - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.GRPC.NetAddr.Endpoint = endpoint - return newReceiver(t, factory, settings, cfg, testReceiverID, tc, mc) -} - -func newReceiver(t *testing.T, factory receiver.Factory, settings component.TelemetrySettings, cfg *Config, id component.ID, tc consumer.Traces, mc consumer.Metrics) component.Component { - set := receivertest.NewNopSettings() - set.TelemetrySettings = settings - set.TelemetrySettings.MetricsLevel = configtelemetry.LevelNormal - set.ID = id - var r component.Component - var err error - if tc != nil { - r, err = factory.CreateTracesReceiver(context.Background(), set, cfg, tc) - require.NoError(t, err) - } - if mc != nil { - r, err = factory.CreateMetricsReceiver(context.Background(), set, cfg, mc) - require.NoError(t, err) - } - return r -} - -type senderFunc func(td ptrace.Traces) - -func TestShutdown(t *testing.T) { - endpointGrpc := testutil.GetAvailableLocalAddress(t) - - nextSink := new(consumertest.TracesSink) - - // Create OTelArrow receiver - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.GRPC.NetAddr.Endpoint = endpointGrpc - set := receivertest.NewNopSettings() - set.ID = testReceiverID - r, err := NewFactory().CreateTracesReceiver( - context.Background(), - set, - cfg, - nextSink) - require.NoError(t, err) - require.NotNil(t, r) - require.NoError(t, r.Start(context.Background(), componenttest.NewNopHost())) - - conn, err := grpc.NewClient(endpointGrpc, grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - defer conn.Close() - - doneSignalGrpc := make(chan bool) - - senderGrpc := func(td ptrace.Traces) { - // Ignore error, may be executed after the receiver shutdown. - _ = exportTraces(conn, td) - } - - // Send traces to the receiver until we signal via done channel, and then - // send one more trace after that. - go generateTraces(senderGrpc, doneSignalGrpc) - - // Wait until the receiver outputs anything to the sink. - assert.Eventually(t, func() bool { - return nextSink.SpanCount() > 0 - }, time.Second, 10*time.Millisecond) - - // Now shutdown the receiver, while continuing sending traces to it. - ctx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second) - defer cancelFn() - err = r.Shutdown(ctx) - assert.NoError(t, err) - - // Remember how many spans the sink received. This number should not change after this - // point because after Shutdown() returns the component is not allowed to produce - // any more data. - sinkSpanCountAfterShutdown := nextSink.SpanCount() - - // Now signal to generateTraces to exit the main generation loop, then send - // one more trace and stop. - doneSignalGrpc <- true - - // Wait until all follow up traces are sent. - <-doneSignalGrpc - - // The last, additional trace should not be received by sink, so the number of spans in - // the sink should not change. - assert.EqualValues(t, sinkSpanCountAfterShutdown, nextSink.SpanCount()) -} - -func generateTraces(senderFn senderFunc, doneSignal chan bool) { - // Continuously generate spans until signaled to stop. -loop: - for { - select { - case <-doneSignal: - break loop - default: - } - senderFn(testdata.GenerateTraces(1)) - } - - // After getting the signal to stop, send one more span and then - // finally stop. We should never receive this last span. - senderFn(testdata.GenerateTraces(1)) - - // Indicate that we are done. - close(doneSignal) -} - -func exportTraces(cc *grpc.ClientConn, td ptrace.Traces) error { - acc := ptraceotlp.NewGRPCClient(cc) - req := ptraceotlp.NewExportRequestFromTraces(td) - _, err := acc.Export(context.Background(), req) - - return err -} - -type errOrSinkConsumer struct { - *consumertest.TracesSink - *consumertest.MetricsSink - mu sync.Mutex - consumeError error // to be returned by ConsumeTraces, if set -} - -// SetConsumeError sets an error that will be returned by the Consume function. -func (esc *errOrSinkConsumer) SetConsumeError(err error) { - esc.mu.Lock() - defer esc.mu.Unlock() - esc.consumeError = err -} - -func (esc *errOrSinkConsumer) Capabilities() consumer.Capabilities { - return consumer.Capabilities{MutatesData: false} -} - -// ConsumeTraces stores traces to this sink. -func (esc *errOrSinkConsumer) ConsumeTraces(ctx context.Context, td ptrace.Traces) error { - esc.mu.Lock() - defer esc.mu.Unlock() - - if esc.consumeError != nil { - return esc.consumeError - } - - return esc.TracesSink.ConsumeTraces(ctx, td) -} - -// ConsumeMetrics stores metrics to this sink. -func (esc *errOrSinkConsumer) ConsumeMetrics(ctx context.Context, md pmetric.Metrics) error { - esc.mu.Lock() - defer esc.mu.Unlock() - - if esc.consumeError != nil { - return esc.consumeError - } - - return esc.MetricsSink.ConsumeMetrics(ctx, md) -} - -// Reset deletes any stored in the sinks, resets error to nil. -func (esc *errOrSinkConsumer) Reset() { - esc.mu.Lock() - defer esc.mu.Unlock() - - esc.consumeError = nil - if esc.TracesSink != nil { - esc.TracesSink.Reset() - } - if esc.MetricsSink != nil { - esc.MetricsSink.Reset() - } -} - -type tracesSinkWithMetadata struct { - consumertest.TracesSink - - lock sync.Mutex - mds []client.Metadata -} - -func (ts *tracesSinkWithMetadata) ConsumeTraces(ctx context.Context, td ptrace.Traces) error { - info := client.FromContext(ctx) - ts.lock.Lock() - defer ts.lock.Unlock() - ts.mds = append(ts.mds, info.Metadata) - return ts.TracesSink.ConsumeTraces(ctx, td) -} - -func (ts *tracesSinkWithMetadata) Metadatas() []client.Metadata { - ts.lock.Lock() - defer ts.lock.Unlock() - return ts.mds -} - -type anyStreamClient interface { - Send(*arrowpb.BatchArrowRecords) error - Recv() (*arrowpb.BatchStatus, error) - grpc.ClientStream -} - -func TestGRPCArrowReceiver(t *testing.T) { - addr := testutil.GetAvailableLocalAddress(t) - sink := new(tracesSinkWithMetadata) - - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.GRPC.NetAddr.Endpoint = addr - cfg.GRPC.IncludeMetadata = true - id := component.NewID(component.MustNewType("arrow")) - tt := componenttest.NewNopTelemetrySettings() - ocr := newReceiver(t, factory, tt, cfg, id, sink, nil) - - require.NotNil(t, ocr) - require.NoError(t, ocr.Start(context.Background(), componenttest.NewNopHost())) - - cc, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - var stream anyStreamClient - client := arrowpb.NewArrowTracesServiceClient(cc) - stream, err = client.ArrowTraces(ctx, grpc.WaitForReady(true)) - require.NoError(t, err) - producer := arrowRecord.NewProducer() - - var headerBuf bytes.Buffer - hpd := hpack.NewEncoder(&headerBuf) - - var expectTraces []ptrace.Traces - var expectMDs []metadata.MD - - // Repeatedly send traces via arrow. Set the expected traces - // metadata to receive. - for i := 0; i < 10; i++ { - td := testdata.GenerateTraces(2) - expectTraces = append(expectTraces, td) - - headerBuf.Reset() - err := hpd.WriteField(hpack.HeaderField{ - Name: "seq", - Value: fmt.Sprint(i), - }) - require.NoError(t, err) - err = hpd.WriteField(hpack.HeaderField{ - Name: "test", - Value: "value", - }) - require.NoError(t, err) - expectMDs = append(expectMDs, metadata.MD{ - "seq": []string{fmt.Sprint(i)}, - "test": []string{"value"}, - }) - - batch, err := producer.BatchArrowRecordsFromTraces(td) - require.NoError(t, err) - - batch.Headers = headerBuf.Bytes() - - err = stream.Send(batch) - - require.NoError(t, err) - - resp, err := stream.Recv() - require.NoError(t, err) - require.Equal(t, batch.BatchId, resp.BatchId) - require.Equal(t, arrowpb.StatusCode_OK, resp.StatusCode) - } - - assert.NoError(t, cc.Close()) - require.NoError(t, ocr.Shutdown(context.Background())) - - assert.Equal(t, expectTraces, sink.AllTraces()) - - assert.Equal(t, len(expectMDs), len(sink.Metadatas())) - // gRPC adds its own metadata keys, so we check for only the - // expected ones below: - for idx := range expectMDs { - for key, vals := range expectMDs[idx] { - require.Equal(t, vals, sink.Metadatas()[idx].Get(key), "for key %s", key) - } - } -} - -type hostWithExtensions struct { - component.Host - exts map[component.ID]component.Component -} - -func newHostWithExtensions(exts map[component.ID]component.Component) component.Host { - return &hostWithExtensions{ - Host: componenttest.NewNopHost(), - exts: exts, - } -} - -func (h *hostWithExtensions) GetExtensions() map[component.ID]component.Component { - return h.exts -} - -func newTestAuthExtension(t *testing.T, authFunc func(ctx context.Context, hdrs map[string][]string) (context.Context, error)) auth.Server { - ctrl := gomock.NewController(t) - as := mock.NewMockServer(ctrl) - as.EXPECT().Authenticate(gomock.Any(), gomock.Any()).AnyTimes().DoAndReturn(authFunc) - return as -} - -func TestGRPCArrowReceiverAuth(t *testing.T) { - addr := testutil.GetAvailableLocalAddress(t) - sink := new(tracesSinkWithMetadata) - - authID := component.NewID(component.MustNewType("testauth")) - - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.GRPC.NetAddr.Endpoint = addr - cfg.GRPC.IncludeMetadata = true - cfg.GRPC.Auth = &configauth.Authentication{ - AuthenticatorID: authID, - } - id := component.NewID(component.MustNewType("arrow")) - tt := componenttest.NewNopTelemetrySettings() - ocr := newReceiver(t, factory, tt, cfg, id, sink, nil) - - require.NotNil(t, ocr) - - const errorString = "very much not authorized" - - type inStreamCtx struct{} - - host := newHostWithExtensions( - map[component.ID]component.Component{ - authID: newTestAuthExtension(t, func(ctx context.Context, _ map[string][]string) (context.Context, error) { - if ctx.Value(inStreamCtx{}) != nil { - return ctx, fmt.Errorf(errorString) - } - return context.WithValue(ctx, inStreamCtx{}, t), nil - }), - }, - ) - - require.NoError(t, ocr.Start(context.Background(), host)) - - cc, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - client := arrowpb.NewArrowTracesServiceClient(cc) - stream, err := client.ArrowTraces(ctx, grpc.WaitForReady(true)) - require.NoError(t, err) - producer := arrowRecord.NewProducer() - - // Repeatedly send traces via arrow. Expect an auth error. - for i := 0; i < 10; i++ { - td := testdata.GenerateTraces(2) - - batch, err := producer.BatchArrowRecordsFromTraces(td) - require.NoError(t, err) - - err = stream.Send(batch) - require.NoError(t, err) - - resp, err := stream.Recv() - require.NoError(t, err) - // The stream has to be successful to get this far. The - // authenticator fails every data item: - require.Equal(t, batch.BatchId, resp.BatchId) - require.Equal(t, arrowpb.StatusCode_UNAUTHENTICATED, resp.StatusCode) - require.Contains(t, resp.StatusMessage, errorString) - } - - assert.NoError(t, cc.Close()) - require.NoError(t, ocr.Shutdown(context.Background())) - - assert.Equal(t, 0, len(sink.AllTraces())) -} - -func TestConcurrentArrowReceiver(t *testing.T) { - addr := testutil.GetAvailableLocalAddress(t) - sink := new(tracesSinkWithMetadata) - - factory := NewFactory() - cfg := factory.CreateDefaultConfig().(*Config) - cfg.GRPC.NetAddr.Endpoint = addr - cfg.GRPC.IncludeMetadata = true - id := component.NewID(component.MustNewType("arrow")) - tt := componenttest.NewNopTelemetrySettings() - ocr := newReceiver(t, factory, tt, cfg, id, sink, nil) - - require.NotNil(t, ocr) - require.NoError(t, ocr.Start(context.Background(), componenttest.NewNopHost())) - - cc, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - const itemsPerStream = 10 - const numStreams = 5 - - var wg sync.WaitGroup - wg.Add(numStreams) - - for j := 0; j < numStreams; j++ { - go func() { - defer wg.Done() - - client := arrowpb.NewArrowTracesServiceClient(cc) - stream, err := client.ArrowTraces(ctx, grpc.WaitForReady(true)) - require.NoError(t, err) - producer := arrowRecord.NewProducer() - - var headerBuf bytes.Buffer - hpd := hpack.NewEncoder(&headerBuf) - - // Repeatedly send traces via arrow. Set the expected traces - // metadata to receive. - for i := 0; i < itemsPerStream; i++ { - td := testdata.GenerateTraces(2) - - headerBuf.Reset() - err := hpd.WriteField(hpack.HeaderField{ - Name: "seq", - Value: fmt.Sprint(i), - }) - require.NoError(t, err) - - batch, err := producer.BatchArrowRecordsFromTraces(td) - require.NoError(t, err) - - batch.Headers = headerBuf.Bytes() - - err = stream.Send(batch) - - require.NoError(t, err) - - resp, err := stream.Recv() - require.NoError(t, err) - require.Equal(t, batch.BatchId, resp.BatchId) - require.Equal(t, arrowpb.StatusCode_OK, resp.StatusCode) - } - }() - } - wg.Wait() - - assert.NoError(t, cc.Close()) - require.NoError(t, ocr.Shutdown(context.Background())) - - counts := make([]int, itemsPerStream) - - // Two spans per stream/item. - require.Equal(t, itemsPerStream*numStreams*2, sink.SpanCount()) - require.Equal(t, itemsPerStream*numStreams, len(sink.Metadatas())) - - for _, md := range sink.Metadatas() { - val, err := strconv.Atoi(md.Get("seq")[0]) - require.NoError(t, err) - counts[val]++ - } - - for i := 0; i < itemsPerStream; i++ { - require.Equal(t, numStreams, counts[i]) - } -} diff --git a/collector/receiver/otelarrowreceiver/testdata/bad_proto_config.yaml b/collector/receiver/otelarrowreceiver/testdata/bad_proto_config.yaml deleted file mode 100644 index 9be62602..00000000 --- a/collector/receiver/otelarrowreceiver/testdata/bad_proto_config.yaml +++ /dev/null @@ -1,3 +0,0 @@ -protocols: - thrift: - endpoint: "127.0.0.1:1234" diff --git a/collector/receiver/otelarrowreceiver/testdata/config.yaml b/collector/receiver/otelarrowreceiver/testdata/config.yaml deleted file mode 100644 index 726263f8..00000000 --- a/collector/receiver/otelarrowreceiver/testdata/config.yaml +++ /dev/null @@ -1,31 +0,0 @@ -protocols: - grpc: - # The following entry demonstrates how to specify TLS credentials for the server. - # Note: These files do not exist. If the receiver is started with this configuration, it will fail. - tls: - cert_file: test.crt - key_file: test.key - - # The following demonstrates how to set maximum limits on stream, message size and connection idle time. - # Note: The test yaml has demonstrated configuration on a grouped by their structure; however, all of the settings can - # be mix and matched like adding the maximum connection idle setting in this example. - max_recv_msg_size_mib: 32 - max_concurrent_streams: 16 - read_buffer_size: 1024 - write_buffer_size: 1024 - - # The following entry configures all of the keep alive settings. These settings are used to configure the receiver. - keepalive: - server_parameters: - max_connection_idle: 11s - max_connection_age: 12s - max_connection_age_grace: 13s - time: 30s - timeout: 5s - enforcement_policy: - min_time: 10s - permit_without_stream: true - arrow: - memory_limit_mib: 123 - admission_limit_mib: 80 - waiter_limit: 100 diff --git a/collector/receiver/otelarrowreceiver/testdata/default.yaml b/collector/receiver/otelarrowreceiver/testdata/default.yaml deleted file mode 100644 index 5c34221b..00000000 --- a/collector/receiver/otelarrowreceiver/testdata/default.yaml +++ /dev/null @@ -1 +0,0 @@ -# The following entry initializes the default otelarrow receiver. diff --git a/collector/receiver/otelarrowreceiver/testdata/only_grpc.yaml b/collector/receiver/otelarrowreceiver/testdata/only_grpc.yaml deleted file mode 100644 index 98f01598..00000000 --- a/collector/receiver/otelarrowreceiver/testdata/only_grpc.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# The following entry initializes the default OTLP receiver with only gRPC support. -protocols: - grpc: diff --git a/collector/receiver/otelarrowreceiver/testdata/typo_default_proto_config.yaml b/collector/receiver/otelarrowreceiver/testdata/typo_default_proto_config.yaml deleted file mode 100644 index 61ffd2b7..00000000 --- a/collector/receiver/otelarrowreceiver/testdata/typo_default_proto_config.yaml +++ /dev/null @@ -1,3 +0,0 @@ -protocols: - grpc: - htttp: diff --git a/collector/receiver/otelarrowreceiver/testdata/uds.yaml b/collector/receiver/otelarrowreceiver/testdata/uds.yaml deleted file mode 100644 index 7e86a719..00000000 --- a/collector/receiver/otelarrowreceiver/testdata/uds.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# The following entry demonstrates how to specify a Unix Domain Socket for the server. -protocols: - grpc: - transport: unix - endpoint: /tmp/grpc_otlp.sock diff --git a/collector/test/e2e_test.go b/collector/test/e2e_test.go index 7fab3ba8..672e366b 100644 --- a/collector/test/e2e_test.go +++ b/collector/test/e2e_test.go @@ -14,8 +14,8 @@ import ( "testing" "time" - "github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter" - "github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver" + "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver" "github.com/open-telemetry/otel-arrow/collector/testutil" "github.com/open-telemetry/otel-arrow/pkg/datagen" "github.com/open-telemetry/otel-arrow/pkg/otel/assert" diff --git a/collector/test/go.mod b/collector/test/go.mod index 7996547f..e68aca1b 100644 --- a/collector/test/go.mod +++ b/collector/test/go.mod @@ -3,16 +3,16 @@ module github.com/open-telemetry/otel-arrow/collector/test go 1.22.2 require ( + github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter v0.104.0 + github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver v0.104.0 github.com/open-telemetry/otel-arrow v0.24.0 github.com/open-telemetry/otel-arrow/collector v0.24.0 - github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter v0.24.0 - github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver v0.24.0 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e - go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c - go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e + go.opentelemetry.io/collector/component v0.104.0 + go.opentelemetry.io/collector/consumer v0.104.0 + go.opentelemetry.io/collector/exporter v0.104.0 + go.opentelemetry.io/collector/pdata v1.11.0 + go.opentelemetry.io/collector/receiver v0.104.0 go.uber.org/zap v1.27.0 google.golang.org/grpc v1.64.0 ) @@ -61,20 +61,20 @@ require ( github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect - go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configcompression v1.9.1-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/confignet v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configopaque v1.9.1-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configretry v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/config/internal v0.102.1 // indirect - go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/extension v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c // indirect - go.opentelemetry.io/collector/featuregate v1.9.0 // indirect + go.opentelemetry.io/collector v0.104.0 // indirect + go.opentelemetry.io/collector/config/configauth v0.104.0 // indirect + go.opentelemetry.io/collector/config/configcompression v1.11.0 // indirect + go.opentelemetry.io/collector/config/configgrpc v0.104.0 // indirect + go.opentelemetry.io/collector/config/confignet v0.104.0 // indirect + go.opentelemetry.io/collector/config/configopaque v1.11.0 // indirect + go.opentelemetry.io/collector/config/configretry v1.11.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.104.0 // indirect + go.opentelemetry.io/collector/config/configtls v0.104.0 // indirect + go.opentelemetry.io/collector/config/internal v0.104.0 // indirect + go.opentelemetry.io/collector/confmap v0.104.0 // indirect + go.opentelemetry.io/collector/extension v0.104.0 // indirect + go.opentelemetry.io/collector/extension/auth v0.104.0 // indirect + go.opentelemetry.io/collector/featuregate v1.11.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect go.opentelemetry.io/otel v1.27.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.49.0 // indirect @@ -85,21 +85,17 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/net v0.26.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver => ../receiver/otelarrowreceiver - -replace github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter => ../exporter/otelarrowexporter - replace github.com/open-telemetry/otel-arrow/collector => ../ replace github.com/open-telemetry/otel-arrow => ../.. diff --git a/collector/test/go.sum b/collector/test/go.sum index b80c9c12..bb78b4ce 100644 --- a/collector/test/go.sum +++ b/collector/test/go.sum @@ -91,6 +91,10 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/mostynb/go-grpc-compression v1.2.3 h1:42/BKWMy0KEJGSdWvzqIyOZ95YcR9mLPqKctH7Uo//I= github.com/mostynb/go-grpc-compression v1.2.3/go.mod h1:AghIxF3P57umzqM9yz795+y1Vjs47Km/Y2FE6ouQ7Lg= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter v0.104.0 h1:5EXODLMCQ+EG3Nt8llNTr4Q5GGmN9tDl0ZSatZfCk6M= +github.com/open-telemetry/opentelemetry-collector-contrib/exporter/otelarrowexporter v0.104.0/go.mod h1:1ApgkABgfYHUKnwXl2JTwM8O5wLcvl8ex7L2NX2vnok= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver v0.104.0 h1:6fpc2nQDCZ9m2w2lh/EMXoGkCQTOpblkNMWuD/jH7jg= +github.com/open-telemetry/opentelemetry-collector-contrib/receiver/otelarrowreceiver v0.104.0/go.mod h1:HMOk2jrjvWeGO3nm3TqLP/fU6x7H30ghOerpQA8atbE= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -120,46 +124,48 @@ github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c h1:UmlCWoLNgxxN906BHOXH06/TMeumOrDqdTXeRkYD6d4= -go.opentelemetry.io/collector v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:gKjweCX6ve4F7X4RGV3kDN24Bg2eyV6MTCncnaPfRPA= -go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c h1:F17okJGeAtqIZZv/7mZvo6gunwPqdlt40znR0Vo1c1Q= -go.opentelemetry.io/collector/component v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:AM5c/Ohhxj2j/vfCZrwKUD7PrcMpuCbo68rSBibV9U4= -go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c h1:dFcfo09PibmEsdBA2sMLbs+IyBWoPg7NwwEG6eWQRfY= -go.opentelemetry.io/collector/config/configauth v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:EJ/PoRVuG1eowA3KQbnW8VonGHGz8jznCXpWG5U8+2A= -go.opentelemetry.io/collector/config/configcompression v1.9.1-0.20240611143128-7dfb57b9ad1c h1:BR/Gtt1BX7psCRIcrw6mIFUUTLfy884WMsQVgoItHeM= -go.opentelemetry.io/collector/config/configcompression v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:6+m0GKCv7JKzaumn7u80A2dLNCuYf5wdR87HWreoBO0= -go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c h1:ZleEsjYf+kxFV5aF8AWHZ4qPFIw/8EQyM2GTnm1ewHo= -go.opentelemetry.io/collector/config/configgrpc v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:Qkn38t0e9y68UlXAWp+S3gsenh09LB9Ct5bJ56inDGQ= -go.opentelemetry.io/collector/config/confignet v0.102.2-0.20240611143128-7dfb57b9ad1c h1:k8bp8JS8b36o3+Pl35cYiSo6pIYV/CW8+etqvRSuoe4= -go.opentelemetry.io/collector/config/confignet v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= -go.opentelemetry.io/collector/config/configopaque v1.9.1-0.20240611143128-7dfb57b9ad1c h1:+OJLmTVoFAzSSYgDW++ltj3ya5ZWjFOlL+sAp3Z4T9U= -go.opentelemetry.io/collector/config/configopaque v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:0xURn2sOy5j4fbaocpEYfM97HPGsiffkkVudSPyTJlM= -go.opentelemetry.io/collector/config/configretry v0.102.2-0.20240611143128-7dfb57b9ad1c h1:1s/JRy/6HKo22PgqsZSRrj3j0KXm0wZPLGZ/rfZlwvU= -go.opentelemetry.io/collector/config/configretry v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:P+RA0IA+QoxnDn4072uyeAk1RIoYiCbxYsjpKX5eFC4= -go.opentelemetry.io/collector/config/configtelemetry v0.102.2-0.20240611143128-7dfb57b9ad1c h1:biIHEgJgIFabkzjRrxyiGs3ZyoJ8jPiJyU8dorKaPWg= -go.opentelemetry.io/collector/config/configtelemetry v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= -go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c h1:Foets1z7XMsh5KwEvGqFhEHem6Kx3xWjfUVUfLk6bF8= -go.opentelemetry.io/collector/config/configtls v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:0/mMXy474cvCd4p4VSiZMTaHD/9LwdGbCqXvBPHkDSg= -go.opentelemetry.io/collector/config/internal v0.102.1 h1:HFsFD3xpHUuNHb8/UTz5crJw1cMHzsJQf/86sgD44hw= -go.opentelemetry.io/collector/config/internal v0.102.1/go.mod h1:Vig3dfeJJnuRe1kBNpszBzPoj5eYnR51wXbeq36Zfpg= -go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c h1:LOhGPowRmdpv7HU6HAFkdRvys41RWaijhGmWa7YBOsg= -go.opentelemetry.io/collector/confmap v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:KgpS7UxH5rkd69CzAzlY2I1heH8Z7eNCZlHmwQBMxNg= -go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c h1:L/FPXl2OoOKniPw1hYzCOk6eljlcwCC681y4plDDE08= -go.opentelemetry.io/collector/consumer v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:4EV8/Rh+KD6z75EjDDWthN50aFeeRqxsC589EpakV5E= -go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e h1:Z5SmlS/YR1O5FGQbXyv0oqNERCDDCMQVUWIIP+0NqM0= -go.opentelemetry.io/collector/exporter v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:JUUGttX6dnz9+LDR+RzH2Imrr04SKwfpdtq42yknbnQ= -go.opentelemetry.io/collector/extension v0.102.2-0.20240611143128-7dfb57b9ad1c h1:kDjy3b4gMdXyYbkvJe2ARcfFsnfOsBLth6s7EB2Gp1s= -go.opentelemetry.io/collector/extension v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:UkgI/9uobPWsyKR17PdindQ4+CDL1hbVgpzUgfp9RRg= -go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c h1:+A3fAo4yg/eDswLz67kny4AlMfFWVY3L44IFbCyxZIk= -go.opentelemetry.io/collector/extension/auth v0.102.2-0.20240611143128-7dfb57b9ad1c/go.mod h1:m7aGTw7yl2Qe5kprqkJeky2wwVKeLYugu6eiS5XcXpY= -go.opentelemetry.io/collector/featuregate v1.9.0 h1:mC4/HnR5cx/kkG1RKOQAvHxxg5Ktmd9gpFdttPEXQtA= -go.opentelemetry.io/collector/featuregate v1.9.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= -go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c h1:f8L2r0f684bJAAZDoTvEWccx34C3kQsePNwy8KzTPqM= -go.opentelemetry.io/collector/pdata v1.9.1-0.20240611143128-7dfb57b9ad1c/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE= -go.opentelemetry.io/collector/pdata/testdata v0.102.1 h1:S3idZaJxy8M7mCC4PG4EegmtiSaOuh6wXWatKIui8xU= -go.opentelemetry.io/collector/pdata/testdata v0.102.1/go.mod h1:JEoSJTMgeTKyGxoMRy48RMYyhkA5vCCq/abJq9B6vXs= -go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e h1:Y1zH80bbNuJWd63T5PjpFs4+NrAl8LvMBxqPCf6XBrM= -go.opentelemetry.io/collector/receiver v0.102.2-0.20240612162315-964e3a95872e/go.mod h1:A/oJHfYc+1auPv6gbp0B/kR8DLtFIDCB/Z/3nDlHVQs= +go.opentelemetry.io/collector v0.104.0 h1:R3zjM4O3K3+ttzsjPV75P80xalxRbwYTURlK0ys7uyo= +go.opentelemetry.io/collector v0.104.0/go.mod h1:Tm6F3na9ajnOm6I5goU9dURKxq1fSBK1yA94nvUix3k= +go.opentelemetry.io/collector/component v0.104.0 h1:jqu/X9rnv8ha0RNZ1a9+x7OU49KwSMsPbOuIEykHuQE= +go.opentelemetry.io/collector/component v0.104.0/go.mod h1:1C7C0hMVSbXyY1ycCmaMUAR9fVwpgyiNQqxXtEWhVpw= +go.opentelemetry.io/collector/config/configauth v0.104.0 h1:ULtjugImijpKuLgGVt0E0HwiZT7+uDUEtMquh1ODB24= +go.opentelemetry.io/collector/config/configauth v0.104.0/go.mod h1:Til+nLLrQwwhgmfcGTX4ZRcNuMhdaWhBW1jH9DLTabQ= +go.opentelemetry.io/collector/config/configcompression v1.11.0 h1:oTwbcLh7mWHSDUIZXkRJVdNAMoBGS39XF68goTMOQq8= +go.opentelemetry.io/collector/config/configcompression v1.11.0/go.mod h1:6+m0GKCv7JKzaumn7u80A2dLNCuYf5wdR87HWreoBO0= +go.opentelemetry.io/collector/config/configgrpc v0.104.0 h1:E3RtqryQPOm/trJmhlJZj6cCqJNKgv9fOEQvSEpzsFM= +go.opentelemetry.io/collector/config/configgrpc v0.104.0/go.mod h1:tu3ifnJ5pv+4rZcaqNWfvVLjNKb8icSPoClN3THN8PU= +go.opentelemetry.io/collector/config/confignet v0.104.0 h1:i7AOTJf4EQox3SEt1YtQFQR+BwXr3v5D9x3Ai9/ovy8= +go.opentelemetry.io/collector/config/confignet v0.104.0/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= +go.opentelemetry.io/collector/config/configopaque v1.11.0 h1:Pt06PXWVmRaiSX63mzwT8Z9SV/hOc6VHNZbfZ10YY4o= +go.opentelemetry.io/collector/config/configopaque v1.11.0/go.mod h1:0xURn2sOy5j4fbaocpEYfM97HPGsiffkkVudSPyTJlM= +go.opentelemetry.io/collector/config/configretry v1.11.0 h1:UdEDD0ThxPU7+n2EiKJxVTvDCGygXu9hTfT6LOQv9DY= +go.opentelemetry.io/collector/config/configretry v1.11.0/go.mod h1:P+RA0IA+QoxnDn4072uyeAk1RIoYiCbxYsjpKX5eFC4= +go.opentelemetry.io/collector/config/configtelemetry v0.104.0 h1:eHv98XIhapZA8MgTiipvi+FDOXoFhCYOwyKReOt+E4E= +go.opentelemetry.io/collector/config/configtelemetry v0.104.0/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= +go.opentelemetry.io/collector/config/configtls v0.104.0 h1:bMmLz2+r+REpO7cDOR+srOJHfitqTZfSZCffDpKfwWk= +go.opentelemetry.io/collector/config/configtls v0.104.0/go.mod h1:e33o7TWcKfe4ToLFyGISEPGMgp6ezf3yHRGY4gs9nKk= +go.opentelemetry.io/collector/config/internal v0.104.0 h1:h3OkxTfXWWrHRyPEGMpJb4fH+54puSBuzm6GQbuEZ2o= +go.opentelemetry.io/collector/config/internal v0.104.0/go.mod h1:KjH43jsAUFyZPeTOz7GrPORMQCK13wRMCyQpWk99gMo= +go.opentelemetry.io/collector/confmap v0.104.0 h1:d3yuwX+CHpoyCh0iMv3rqb/vwAekjSm4ZDL6UK1nZSA= +go.opentelemetry.io/collector/confmap v0.104.0/go.mod h1:F8Lue+tPPn2oldXcfqI75PPMJoyzgUsKVtM/uHZLA4w= +go.opentelemetry.io/collector/consumer v0.104.0 h1:Z1ZjapFp5mUcbkGEL96ljpqLIUMhRgQQpYKkDRtxy+4= +go.opentelemetry.io/collector/consumer v0.104.0/go.mod h1:60zcIb0W9GW0z9uJCv6NmjpSbCfBOeRUyrtEwqK6Hzo= +go.opentelemetry.io/collector/exporter v0.104.0 h1:C2HmnfBa05IQ2T+p9T7K7gXVxjrBLd+JxEtAWo7JNbg= +go.opentelemetry.io/collector/exporter v0.104.0/go.mod h1:Rx0oB0E4Ccg1JuAnEWwhtrq1ygRBkfx4mco1DpR3WaQ= +go.opentelemetry.io/collector/extension v0.104.0 h1:bftkgFMKya/QIwK+bOxEAPVs/TvTez+s1mlaiUznJkA= +go.opentelemetry.io/collector/extension v0.104.0/go.mod h1:x7K0KyM1JGrtLbafEbRoVp0VpGBHpyx9hu87bsja6S4= +go.opentelemetry.io/collector/extension/auth v0.104.0 h1:SelhccGCrqLThPlkbv6lbAowHsjgOTAWcAPz085IEC4= +go.opentelemetry.io/collector/extension/auth v0.104.0/go.mod h1:s3/C7LTSfa91QK0JPMTRIvH/gCv+a4DGiiNeTAX9OhI= +go.opentelemetry.io/collector/featuregate v1.11.0 h1:Z7puIymKoQRm3oNM/NH8reWc2zRPz2PNaJvuokh0lQY= +go.opentelemetry.io/collector/featuregate v1.11.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= +go.opentelemetry.io/collector/pdata v1.11.0 h1:rzYyV1zfTQQz1DI9hCiaKyyaczqawN75XO9mdXmR/hE= +go.opentelemetry.io/collector/pdata v1.11.0/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE= +go.opentelemetry.io/collector/pdata/pprofile v0.104.0 h1:MYOIHvPlKEJbWLiBKFQWGD0xd2u22xGVLt4jPbdxP4Y= +go.opentelemetry.io/collector/pdata/pprofile v0.104.0/go.mod h1:7WpyHk2wJZRx70CGkBio8klrYTTXASbyIhf+rH4FKnA= +go.opentelemetry.io/collector/pdata/testdata v0.104.0 h1:BKTZ7hIyAX5DMPecrXkVB2e86HwWtJyOlXn/5vSVXNw= +go.opentelemetry.io/collector/pdata/testdata v0.104.0/go.mod h1:3SnYKu8gLfxURJMWS/cFEUFs+jEKS6jvfqKXnOZsdkQ= +go.opentelemetry.io/collector/receiver v0.104.0 h1:URL1ExkYYd+qbndm7CdGvI2mxzsv/pNfmwJ+1QSQ9/o= +go.opentelemetry.io/collector/receiver v0.104.0/go.mod h1:+enTCZQLf6dRRANWvykXEzrlRw2JDppXJtoYWd/Dd54= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= @@ -206,8 +212,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -222,8 +228,8 @@ golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -231,8 +237,8 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/versions.yaml b/versions.yaml index 27dcc7bf..4ec57e6e 100644 --- a/versions.yaml +++ b/versions.yaml @@ -9,8 +9,6 @@ module-sets: - github.com/open-telemetry/otel-arrow/collector - github.com/open-telemetry/otel-arrow/collector/cmd/otelarrowcol - github.com/open-telemetry/otel-arrow/collector/examples/printer - - github.com/open-telemetry/otel-arrow/collector/exporter/otelarrowexporter - - github.com/open-telemetry/otel-arrow/collector/receiver/otelarrowreceiver - github.com/open-telemetry/otel-arrow/collector/connector/validationconnector - github.com/open-telemetry/otel-arrow/collector/exporter/fileexporter - github.com/open-telemetry/otel-arrow/collector/processor/concurrentbatchprocessor