Repository navigation
Conversation
468278a to
b55b23c
Compare
|
@krishicks I rebased this onto current Since #4399 landed, the gateway exporter handles gzip and https while the relay hop still doesn't. That's the next step in #2642, where the gateway, supervisor and relay share one channel. I'd rather keep that out of this PR so it stays reviewable. This PR hasn't run CI yet. Could you |
|
/ok to test b55b23c |
Compatibility review (advisory)Unaddressed breaking changes The candidate silently replaces explicit sandbox-level OTLP protocol and compression environment values when no endpoint is supplied. This violates the v0.1.5 patch-train policy, and the PR discussion does not explicitly acknowledge affected existing consumers. 1. api-behavior — NOT ACKED
Review details
Advisory review; publication gates are unchanged. |
|
The way #4500 was implemented, the gateway resolves the OTLP endpoint configuration for both traces and OCSF events as possibly different values based on the config, and hands this same config to the supervisor. This is because OCSF logs may end up going to a different place than operational traces. There's a similar concern with agent traces; these may want to go somewhere else. I think that means we should extend the configuration in the gateway to add another table specifically for where agent traces should go, similar to the table for OCSF events. This would be an optional override. That would give operators the ultimate flexibility in their deployment: ability to scale collector pools independently for different use cases, and also to re-export to different sinks (like an SIEM, or LGTM stack) easily. You wouldn't need to do it as part of this PR, but a fast-follow PR would be good. What do you think? |
|
I had Opus 5.5 review this and it came up with some legitimate (I think) concerns. Of these, I think probably 1 and 2 are worth addressing in this PR; the others could be follow-ups. 1. A sandbox user who wants to export to their own collector can't while the relay is active. Possible resolution: inject only the generic 2. Agents get OTLP variables even when there's nowhere to send them, and metrics and logs exports go to the relay too. Today a sandbox without a collector gets no OTLP variables. With this PR every workload gets them, so SDKs that turn on export when an endpoint is set retry against a denied address for the life of the process. And because the generic endpoint is set, agents with metrics or logs enabled send Possible resolution: inject only when the relay is active, which needs the supervisor to signal the sandbox runtime. For metrics and logs, accept and discard those paths, or document the behavior. 3. The relay acknowledges spans before delivering them, so they can be lost while the agent believes they were delivered. The relay answers 200 once a batch is buffered, then makes one export attempt and discards the batch on failure. A collector restart or an Possible resolution: retry once on 4. An unhealthy collector delays every sandbox's exit report by about 5 seconds. The flush before the exit report waits until the buffer drains or the flush timeout runs out. If the collector silently drops connections, each export spends the connect timeout and the buffer never drains, so every exit is reported late for as long as the collector is down. Possible resolution: skip or shorten the flush when the most recent export failed, so a trace sidecar's health doesn't affect lifecycle reporting. 5. Operators get no log when buffered spans are lost on supervisor error paths. If the sandbox run exits early with an error after the relay starts, the relay is dropped and its export task aborted. The batches still pending aren't counted as failures, and the final summary only logs when there are failures, so nothing is logged. Possible resolution: count 6. A collector endpoint that works for the supervisor's own spans can turn the relay off. Possible resolution: parse the endpoint the same way 7. An agent can still label its spans with another sandbox's id. Resource attributes are rewritten correctly, including when the resource is split across repeated fields. Span and scope attributes pass through unchanged, though, and unscoped queries such as Tempo's Possible resolution: strip the two attribution keys at scope and span level as well, or narrow the claim. Everything below this is a nit, I think: Smaller items
|
|
Agreed, that's exactly where I was heading too. I'd keep a single endpoint in I'll do this as a fast-follow PR once this one lands. |
|
Thanks for the thorough review. I went through each point against the code and they hold up, so here's my plan. 1 and 2, env injection. Very good point on 1, I'll switch to your suggestion. The relay will inject only the generic For 2, the supervisor will signal the sandbox runtime so the variables are only injected while the relay is actually active. One caveat remains: with the generic endpoint set, agents that export metrics or logs will send 4, 5, 6, 7, and the nits on 3, retrying on The |
b55b23c to
3559561
Compare
|
/ok-to-test 3559561 |
Compatibility review (advisory)New or updated breaking changes; see the full report for all findings. 1. api-behavior — No explicit risk acceptance found
Review details
Advisory review; publication gates are unchanged. |
Serve the reserved endpoint 192.0.0.8:4318 inside the supervisor, attribute each batch with openshell.sandbox.id and openshell.telemetry.source=agent at the protobuf wire level, buffer it with fixed bounds, and forward it over OTLP/gRPC to the collector the supervisor already uses, flushing before the exit report and keeping the relay up for exec sessions. Signed-off-by: Roland Huß <rhuss@redhat.com>
…nnel Expose Exporter::with_channel as the seam for a collector transport built once in openshell-otel and shared by the gateway, the supervisor's own spans, and the relay. Exporter::new keeps building the plaintext channel for now. Signed-off-by: Roland Huß <rhuss@redhat.com>
Inject the relay variables only when the sandbox creation request names no OTLP endpoint, generic or traces-specific. A caller who names one has chosen where the agent exports; overwriting it would silently redirect an agent that deliberately exports elsewhere. A protocol or compression setting alone does not suppress injection. Applies to both spawn paths and to exec children, with the docs updated to match. Signed-off-by: Roland Huß <rhuss@redhat.com>
Inject only the generic OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_PROTOCOL as lowest-precedence defaults, and only while the relay is active. The supervisor now starts the relay before attach and signals it to the sandbox runtime through AgentSpec::otlp_relay_active. An endpoint in the creation request, the image ENV, or an exec environment suppresses the defaults, and a provider-supplied traces endpoint is removed. Harden the relay itself: - skip the exit flush and shutdown wait when the last export failed - count batches still pending on Drop as discarded and log them - validate the collector endpoint with the shared openshell-otel helper - accept Content-Encoding: identity - fix the 413 and shutdown 503 messages and the memory bound docs - narrow the attribution claim to resource attributes Document best-effort delivery and the 404 for metrics and logs. Signed-off-by: Roland Huß <rhuss@redhat.com>
The relay started whenever a sandbox supervisor had a trace endpoint, so enabling gateway trace export also changed every workload's environment. Make it opt-in: `[openshell.gateway.otlp.agent]` with `enabled = true` turns it on, and its optional `endpoint` falls back to the shared `[openshell.gateway.otlp]` endpoint, like the OCSF table. The gateway validates the endpoint and drivers pass it to supervisors as OPENSHELL_OTLP_AGENT_ENDPOINT, separate from OPENSHELL_OTLP_ENDPOINT. Docker, Podman (including the Podman Machine host alias), Kubernetes and VM drivers carry it, in-process and as standalone binaries. The shared Kubernetes supervisor still does not relay. The relay now acknowledges OTLP metrics and logs exports with an empty success and discards them, so agent SDKs that export every signal to the generic endpoint stop retrying. Discards are counted and logged once. Local dev gateway tasks, the Skaffold values and the e2e harness enable the table; OPENSHELL_E2E_OTLP_AGENT=0 runs the inert case with trace export still on. Signed-off-by: Roland Huß <rhuss@redhat.com>
3559561 to
80a2f8a
Compare
|
Rebased onto main after #4701/#4702 and settled the two open points. The relay is now opt-in through For Main moved the sandbox protocol to protobuf, so Could you |
Add an "Agent traces" section to the architecture page and document the relay in the crate READMEs: the reserved-destination hook in the network supervisor, the OTEL defaults the sandbox runtime injects, and how the Docker, Podman, Kubernetes, and VM drivers pass the agent endpoint. Signed-off-by: Roland Huß <rhuss@redhat.com>
Summary
Let the supervisor forward a sandboxed agent's OpenTelemetry traces to an OTLP collector, so operators see agent spans next to infrastructure spans without a sandbox egress rule. Operators opt in with
[openshell.gateway.otlp.agent]. The agent exports to a reserved address inside the sandbox; the supervisor serves that address, attributes each batch with the sandbox id, and forwards it over OTLP/gRPC.This is the minimal first iteration agreed on #3196 and supersedes that PR: the gateway hop, session protocol changes, and capability negotiation are gone because the compute driver hands the supervisor the collector endpoint directly (#3977). Part of #2641.
Related Issue
Closes #4275
Changes
[openshell.gateway.otlp.agent]table withenabled(defaultfalse) and an optionalendpointthat falls back to[openshell.gateway.otlp].endpoint, like the OCSF table. The gateway validates it at startup. Without the table, or withenabled = false, the workload environment is unchanged even when trace export is on.OPENSHELL_OTLP_AGENT_ENDPOINT(--otlp-agent-endpoint), separate from the supervisor's own trace endpoint. On Podman Machine the driver rewrites a loopback agent endpoint to the desktop host, as it does for the trace endpoint. The shared Kubernetes supervisor (feat(kubernetes): host sandboxes on the shared supervisor #4702) does not relay; its startup warning says so.AgentSpecgainsbool otlp_relay_active = 6, which tells the sandbox to inject the agent variables.openshell-sandbox: when the relay is active, every workload process (main, exec, SSH) getsOTEL_EXPORTER_OTLP_ENDPOINT=http://192.0.0.8:4318,OTEL_EXPORTER_OTLP_PROTOCOL=http/protobufas defaults applied after the provider variables, and an inheritedOTEL_EXPORTER_OTLP_TRACES_ENDPOINTis removed. When the creation request already names an OTLP endpoint, generic or traces-specific, nothing is injected and the caller's values stand, per the guidance on feat(observability): supervisor OTLP telemetry relay #3196. The collector address is never visible in the sandbox.openshell-supervisor-network: a genericReservedDestinationhook at the top ofpreauthorize_transparent_openserves staged opens to the reserved address inside the supervisor, ahead of host mapping, policy, and SSRF checks. Without a handler the address is denied immediately, logged once at warn.openshell-supervisor::otlp_relay: hyper HTTP/1.1 receiver (POST /v1/traces, protobuf only; 404, 408, 413, 415, 503 withRetry-After, all withgoogle.rpc.Statusbodies); wire-level enrichment that decodes only eachResourceSpans.resourceand copies spans byte for byte, replacingopenshell.sandbox.idandopenshell.telemetry.source=agent; bounded buffer; one export task sending rawExportTraceServiceRequestbytes through a lazy tonic channel with keepalive-based dead-connection detection.Exporter::with_channelis the seam for the shared collector transport requested on feat(observability): supervisor OTLP telemetry relay #3196.POST /v1/metricsand/v1/logsare drained and answered with an empty OTLP success so SDKs that export every signal do not retry; discards are counted, the first is logged, and the count appears in the summaries. FR-009 log cadence: first-occurrence warnings, 30 s summaries while counts change, final summary.ResourceSpansentries, 256 KiB of resources and 8192 decoded elements per request, 32 buffered batches, 5 s per export, 5 s flush. Worst-case memory attributable to the agent is about 111 MiB.https, or the sandbox id is missing.openshell-otel-test-support: resource attributes and duplicate-key tracking onReceivedTraces,start_on/try_start_on, and arelaymodule with the attribution keys and a request builder.tests/otlp_relay.rs(span with attribution, inert without a collector, flush before exit);OPENSHELL_E2E_OTLP_ENDPOINTopt-in in the podman and docker gateway wrappers, which also enable[openshell.gateway.otlp.agent]unlessOPENSHELL_E2E_OTLP_AGENT=0;otlp_relayinPODMAN_CI_TESTS.tasks/scripts/gateway*.sh) anddeploy/helm/openshell/ci/values-skaffold.yamlenable the agent table.docs/how-it-works/gateways/configuration.mdx(including the new table), an "Agent traces" section indocs/about/architecture.mdx, the supervisor, network supervisor, sandbox, and Docker, Podman, Kubernetes, and VM driver READMEs, and thedebug-openshell-clusterandhelm-dev-environmentskills.Deferred, as agreed on #3196 and tracked on #2641: TLS and authentication on the relayed hop (one shared transport in
openshell-otel), relay on the shared Kubernetes supervisor, retry onUnavailable, batching and tunable limits, forwarding agent metrics and logs, additional attribution attributes, JSON or gRPC receivers and gzip bodies, operator-configurable connection cap, OCSF event relay.Review callout: when the relay is inactive, an export to the reserved address is denied without an OCSF network-denial event (a standard SDK retries every few seconds for the life of the agent). The inactive state is recorded once at startup as a
ConfigStateChange; the proxy logs the first denied attempt at warn and later ones at debug.Release Notes
Sandboxed agents can send OpenTelemetry traces to your collector without an egress rule. Add
[openshell.gateway.otlp.agent]withenabled = truetogateway.toml; the supervisor tags each span with the sandbox id and forwards it to the agent endpoint, or to the shared OTLP endpoint when none is set.Testing
cargo test --libandcargo clippy --testsfor the touched crates: clippy clean, all tests pass except the known load-sensitiveproxy::tests::mediated_connect_keeps_workload_bytes_read_with_the_synthesized_header, which passes 10 of 10 when run alone on both this branch andmain[otlp.agent]parsing, endpoint fallback and validation,AgentSpecprotobuf round trip ofotlp_relay_active, receiver status codes andgoogle.rpc.Statusbodies, metrics and logs acknowledged and discarded, limits and back-pressure, wire-level enrichment (verbatim copy, byte and element budgets, nesting depth), exporter timeout and dead-connection detection, lifecycle flush and shutdown, proxy reserved-destination hook, sandbox environment composition for both injection branches in a re-executed test binary--no-fail-fast. All threeotlp_relaytests pass. Every other failure either also fails onmain(9a85667) in this environment or passes on rerun; the failing targets were rerun on this branch with the relay on and off and onmainwith the same resultsCLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1,OTEL_TRACES_EXPORTER=otlp) and a Python OpenTelemetry SDK agent. Traces reached Tempo through an OpenTelemetry Collector withopenshell.sandbox.idandopenshell.telemetry.source=agent; Claude Code'sinteraction,llm_requestandtoolspans arrived intact.Checklist
docs/about/architecture.mdxand crate READMEs;architecture/was removed in docs: remove the architecture directory #3799)