52 lines
1.8 KiB
Go
52 lines
1.8 KiB
Go
package tracing
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
|
"go.opentelemetry.io/otel/propagation"
|
|
"go.opentelemetry.io/otel/trace"
|
|
"k8s.io/client-go/transport"
|
|
)
|
|
|
|
// RestConfigWrapper returns a rest.Config.Wrap-compatible wrapper that adds
|
|
// a client span to every Kubernetes API request already running inside a
|
|
// trace. Requests with no parent span pass through untouched: informer
|
|
// list/watch long-polls, leader-election renewals, and metrics authn would
|
|
// otherwise each become meaningless (and in the watch case, minutes-long)
|
|
// root spans. client-go applies Wrap innermost, so the span sees the final
|
|
// authenticated request.
|
|
func RestConfigWrapper(opts ...Option) transport.WrapperFunc {
|
|
o := newOptions(opts)
|
|
return func(rt http.RoundTripper) http.RoundTripper {
|
|
otelOpts := []otelhttp.Option{
|
|
// Explicit propagators: deterministic regardless of global
|
|
// state. The apiserver ignores incoming traceparent (by
|
|
// design), so injection is harmless there.
|
|
otelhttp.WithPropagators(propagation.NewCompositeTextMapPropagator(
|
|
propagation.TraceContext{}, propagation.Baggage{})),
|
|
}
|
|
if o.tp != nil {
|
|
otelOpts = append(otelOpts, otelhttp.WithTracerProvider(o.tp))
|
|
}
|
|
return &parentGatedTransport{
|
|
traced: otelhttp.NewTransport(rt, otelOpts...),
|
|
plain: rt,
|
|
}
|
|
}
|
|
}
|
|
|
|
// parentGatedTransport enforces the parent-span requirement itself rather
|
|
// than relying on otelhttp filter semantics for transports.
|
|
type parentGatedTransport struct {
|
|
traced http.RoundTripper
|
|
plain http.RoundTripper
|
|
}
|
|
|
|
func (t *parentGatedTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
if trace.SpanContextFromContext(r.Context()).IsValid() {
|
|
return t.traced.RoundTrip(r)
|
|
}
|
|
return t.plain.RoundTrip(r)
|
|
}
|