Repository navigation
feat(observability): export OTLP over HTTP/protobuf to any collector - #5082
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
|
🌿 Preview your docs: https://nvidia-preview-pr-5082.docs.buildwithfern.com/openshell |
0d79a30 to
26f001e
Compare
rhuss
left a comment
There was a problem hiding this comment.
cc-review Summary
This review comes from an agent-assisted pass (cc-review, with several reviewer agents plus Codex), and the PR holds up well: the new HTTP transport is tightly bounded and the config rules fail closed. All ten findings are Minor and non-blocking. The failure scenarios below were derived by reading the code; none of them were executed.
What Went Well
OtlpHttpClient::sendwraps each request inAbortOnDrop, bounds it with a timeout and caps the response at 64 KiB throughLimited.abandoned_requests_stop,truncated_responses_failandoversized_responses_faileach pin one of those guarantees directly.TryFrom<RawOtlpConfig>makes a lane inherit the whole shared collector or none of it and rejects a protocol without its own endpoint.OtlpCollectorConfig::checkedrefuses https withhttp/protobufat load time instead of downgrading to plaintext, andotlp_lane_protocol_needs_its_own_endpointcovers every rejection branch.SupervisorOtlp::environmentandmap_endpointsreplace the per-driver env assembly and the Podman loopback-rewrite helpers with one shared type. The_PROTOCOLvariable is emitted only for non-gRPC lanes (andvm.rsfollows the same rule for CLI flags), so gRPC deployments keep a byte-identical supervisor environment and older supervisors keep working.- The relay's
Exporterforwards agent batches as opaque pre-encoded bytes on both transports and sets the HTTP client deadline toEXPORT_TIMEOUT + CLIENT_TIMEOUT_MARGIN, soExportError::Timeoutmeans the same thing for gRPC and HTTP.
Findings
| Severity | File | Description | Source |
|---|---|---|---|
| Minor | crates/openshell-driver-docker/README.md:13 |
Docker README and the otel_tracing.rs module doc still describe export as OTLP/gRPC only |
architecture agent |
| Minor | crates/openshell-driver-docker/src/main.rs:48 |
OTLP lane flags and supervisor_otlp() are copied into all four driver binaries |
architecture agent |
| Minor | crates/openshell-otel/src/lib.rs:261 |
The HttpsUnsupported guard in http_signal_url has no test |
test-quality agent |
| Minor | crates/openshell-otel/src/logs.rs:39 |
Forwarded HTTP collector headers are percent-decoded twice | codex |
| Minor | crates/openshell-otel/src/transport.rs:4 |
Module doc says every exporter shares it; connect and keepalive constants are duplicated in the relay | architecture agent |
| Minor | crates/openshell-otel/src/transport.rs:32 |
export_timeout lookup order and fallback are untested |
test-quality agent |
| Minor | crates/openshell-otel/src/transport.rs:39 |
Query strings now carry working credentials over HTTP, but endpoints are logged verbatim | security agent |
| Minor | crates/openshell-supervisor/src/main.rs:569 |
Supervisor OTLP startup logs omit the selected protocol (outside the diff) | gator agent |
| Minor | crates/openshell-supervisor/src/otlp_relay/exporter.rs:155 |
No silent-collector test for the HTTP relay exporter | test-quality agent |
| Minor | deploy/helm/openshell/values.yaml:395 |
Helm comment advertises 4318 for http/protobuf, but server.otlp has no way to set a protocol |
gator agent (also goal-alignment agent) |
Outside the diff: The supervisor's OTLP exporting enabled and OTLP OCSF export enabled startup logs (crates/openshell-supervisor/src/main.rs:569 and 574) report only the endpoint, while the gateway's equivalent log now carries *_protocol fields. The protocol comes from an env var that is absent for gRPC (and ignored by older supervisor images), so the log can't tell those cases apart, and a transport mismatch only shows up later as export failures. Adding protocol = %args.otlp_protocol and protocol = %args.otlp_ocsf_protocol to the two info! calls would close the gap; it sits here rather than inline because those lines are not part of the changed hunks.
PR description note: The description says "HTTP exports are gzip-compressed", but SDK exporters compress only with OTEL_EXPORTER_OTLP_COMPRESSION=gzip (supervisors never receive OTEL_* variables) and the relay's export_http never compresses. Suggest rewording, or adding gzip to the relay if intended.
Out of scope here: TLS for http/protobuf (https endpoints, cleartext OCSF headers) is left to #5084.
Nice work overall. These are suggestions, not blockers, and the PR should be ready for a maintainer once they are addressed or answered.
Review Details
- Findings posted: 10 (9 inline, 1 summary-only)
- Findings reviewed and not posted: 4
- Gate outcome: PASS
- Participating reviewers: architecture, test-quality, security, gator, goal-alignment, codex (CodeRabbit timed out; correctness and production reported no posted findings)
Coverage note: one external reviewer (CodeRabbit) timed out, so this review is marked incomplete.
26f001e to
c7c70a7
Compare
Every OTLP export (gateway traces, OCSF logs, supervisor traces, and the agent trace relay) spoke gRPC only, so trace backends that accept only OTLP/HTTP needed a translating collector in between. Each [openshell.gateway.otlp] table accepts protocol = "grpc" or "http/protobuf". A table without its own endpoint uses the whole shared collector; a table with its own endpoint inherits nothing from it. Compute drivers take the collectors through one shared set of flags, read from the same environment variables that supervisors receive, and pass them on through one SupervisorOtlp value that replaces the per-lane driver fields. OTLP/HTTP goes through a single client in openshell-otel, shared by the SDK exporters and the relay, that bounds each request by the export timeout and fails exports whose response is incomplete or larger than 64 KiB. Exporters send no query parameters, so an endpoint with a query string is a configuration error. Refs #5067 Signed-off-by: Kris Hicks <khicks@nvidia.com>
c7c70a7 to
b1f0313
Compare
Compatibility review (advisory)New or updated breaking changes; see the full report for all findings. 1. configuration — No explicit risk acceptance foundGateway TOML previously accepted OTLP endpoint URIs containing query strings, but now rejects them during configuration loading, including default gRPC configurations.
Review details
Advisory review; publication gates are unchanged. |
TaylorMutch
left a comment
There was a problem hiding this comment.
Reviewed the updated head b1f0313, including the shared driver flags, endpoint validation, header escaping, and collector timeout coverage. No blocking findings.
Local validation on this head: all 48 openshell-otel tests passed, along with the targeted sandbox_env tests in openshell-core with the clap feature enabled.
The earlier head c7c70a7 also passed a local Docker sandbox smoke test: three agent export/exit cycles with correct attribution and delivery before completion, plus HTTP/protobuf traces from the gateway, Docker driver, supervisor, and agent, OCSF logs, signal paths, and configured gzip compression. The end-to-end smoke test was not repeated on the updated head.
rhuss
left a comment
There was a problem hiding this comment.
cc-review found no issues at b1f0313.
Summary
Every OTLP export spoke gRPC only, so trace backends that accept only OTLP/HTTP, such as MLflow, needed a translating collector in between. Each
[openshell.gateway.otlp]table now takesprotocol = "grpc"or"http/protobuf", honored by gateway traces, OCSF logs, supervisor traces, the agent trace relay, and compute drivers' own spans.Related Issue
Refs #5067. The next PR in this stack, which adds collector headers, closes it.
Changes
protocolon[openshell.gateway.otlp]and itstraces,ocsf, andagenttables. A table without its ownendpointinherits the whole shared collector; a table with its ownendpointinherits nothing from it.SupervisorOtlpArgsflag set inopenshell-core, behind a newclapfeature. The flags read the same environment variables that supervisors receive, and the gateway's VM launcher uses the exported flag names. Drivers pass each lane to supervisors through oneSupervisorOtlpvalue (OPENSHELL_OTLP_PROTOCOL,OPENSHELL_OTLP_OCSF_PROTOCOL,OPENSHELL_OTLP_AGENT_PROTOCOL), replacing the per-lane driver fields.openshell-otel, shared by the SDK exporters and the relay, posts to/v1/tracesor/v1/logsunder the endpoint and bounds each request by the export timeout. A response that is cut short or larger than 64 KiB fails the export rather than counting as success. SDK exporters gzip their requests only whenOTEL_EXPORTER_OTLP_COMPRESSION=gzipis set; the relay forwards batches uncompressed.e2e/rustandtests/suites/featureslockfiles pick upopentelemetry-http.debug-openshell-clusterskill are updated. The Helm description points togatewayConfig."openshell.gateway.otlp".protocolfor anhttp/protobufcollector.Release Notes
OTLP export can use OTLP/HTTP: set
protocol = "http/protobuf"on[openshell.gateway.otlp]or any of its signal tables. gRPC stays the default. An OTLP endpoint with a query string is now a configuration error.Testing
cargo fmt --check, and everymise run rust:lintclippy invocation with--lockedcargo test -p openshell-otel,-p openshell-supervisor,-p openshell-core --features clap, the gateway VM launcher tests, and the server OTLP and config testsTests for the other touched crates ran at the top of the stack.
Checklist