Bug description
MemoryRESTClientGetter.ToRESTConfig() calls c.cfg.Wrap() unconditionally on every invocation. Because rest.Config.Wrap() is additive and the config is shared across the getter's lifetime, each call stacks another retryingRoundTripper layer around the transport.
The retrying round-tripper was added in #1084 to fix temporary etcdserver: leader changed errors from kube-apiserver. However, the Wrap call was placed inside ToRESTConfig() without a guard, so it fires on every call rather than once.
Impact
- Each reconciliation invokes
ToRESTConfig() multiple times (via ToDiscoveryClient, ToRESTMapper, and direct calls).
- Over time, the nesting depth grows linearly: after N calls, each HTTP request traverses N retry layers.
- This causes memory growth from retained closure/wrapper chains and deepening call stacks.
Steps to reproduce
- Create a
MemoryRESTClientGetter with a *rest.Config.
- Call
ToRESTConfig() 10 times.
- Inspect
cfg.WrapTransport(nil) and walk the retryingRoundTripper chain.
- Observe 10 nested layers instead of 1.
Expected behavior
The transport should be wrapped exactly once regardless of how many times ToRESTConfig() is called.
Proposed fix
Guard the c.cfg.Wrap() call with a sync.Once field on the struct, consistent with the existing lazy-init patterns already used in the struct (sync.Mutex for discoveryClient, restMapper, clientCfg).
Bug description
MemoryRESTClientGetter.ToRESTConfig()callsc.cfg.Wrap()unconditionally on every invocation. Becauserest.Config.Wrap()is additive and the config is shared across the getter's lifetime, each call stacks anotherretryingRoundTripperlayer around the transport.The retrying round-tripper was added in #1084 to fix temporary
etcdserver: leader changederrors from kube-apiserver. However, theWrapcall was placed insideToRESTConfig()without a guard, so it fires on every call rather than once.Impact
ToRESTConfig()multiple times (viaToDiscoveryClient,ToRESTMapper, and direct calls).Steps to reproduce
MemoryRESTClientGetterwith a*rest.Config.ToRESTConfig()10 times.cfg.WrapTransport(nil)and walk theretryingRoundTripperchain.Expected behavior
The transport should be wrapped exactly once regardless of how many times
ToRESTConfig()is called.Proposed fix
Guard the
c.cfg.Wrap()call with async.Oncefield on the struct, consistent with the existing lazy-init patterns already used in the struct (sync.Mutexfor discoveryClient, restMapper, clientCfg).