@@ -21,14 +21,12 @@ import (
2121 "fmt"
2222 "net"
2323 "net/http"
24- "runtime"
2524 "strings"
2625 "sync"
2726 "time"
28- "weak"
2927
3028 utilnet "k8s.io/apimachinery/pkg/util/net"
31- clientgofeaturegate "k8s.io/client-go/features "
29+ "k8s.io/apimachinery/pkg/util/wait "
3230 "k8s.io/client-go/tools/metrics"
3331 "k8s.io/klog/v2"
3432)
@@ -37,20 +35,19 @@ import (
3735// same RoundTripper will be returned for configs with identical TLS options If
3836// the config has no custom TLS options, http.DefaultTransport is returned.
3937type tlsTransportCache struct {
40- mu sync.Mutex
41- transports map [tlsCacheKey ]weak.Pointer [trackedTransport ] // GC-enabled
42- strongTransports map [tlsCacheKey ]http.RoundTripper // GC-disabled
38+ mu sync.Mutex
39+ transports map [tlsCacheKey ]http.RoundTripper
4340}
4441
45- const idleConnsPerHost = 25
42+ // DialerStopCh is stop channel that is passed down to dynamic cert dialer.
43+ // It's exposed as variable for testing purposes to avoid testing for goroutine
44+ // leakages.
45+ var DialerStopCh = wait .NeverStop
4646
47- var tlsCache = newTLSCache ()
47+ const idleConnsPerHost = 25
4848
49- func newTLSCache () * tlsTransportCache {
50- return & tlsTransportCache {
51- transports : make (map [tlsCacheKey ]weak.Pointer [trackedTransport ]),
52- strongTransports : make (map [tlsCacheKey ]http.RoundTripper ),
53- }
49+ var tlsCache = & tlsTransportCache {
50+ transports : make (map [tlsCacheKey ]http.RoundTripper ),
5451}
5552
5653type tlsCacheKey struct {
@@ -88,18 +85,14 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
8885 // Ensure we only create a single transport for the given TLS options
8986 c .mu .Lock ()
9087 defer c .mu .Unlock ()
91- defer func () { metrics .TransportCacheEntries .Observe (c . lenLocked ()) }( )
88+ defer metrics .TransportCacheEntries .Observe (len ( c . transports ) )
9289
9390 // See if we already have a custom transport for this config
94- if t , ok := c .getLocked (key ); ok {
95- if t != nil {
96- metrics .TransportCreateCalls .Increment ("hit" )
97- return t , nil
98- }
99- metrics .TransportCreateCalls .Increment ("miss-gc" )
100- } else {
101- metrics .TransportCreateCalls .Increment ("miss" )
91+ if t , ok := c .transports [key ]; ok {
92+ metrics .TransportCreateCalls .Increment ("hit" )
93+ return t , nil
10294 }
95+ metrics .TransportCreateCalls .Increment ("miss" )
10396 } else {
10497 metrics .TransportCreateCalls .Increment ("uncacheable" )
10598 }
@@ -126,17 +119,14 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
126119
127120 // If we use are reloading files, we need to handle certificate rotation properly
128121 // TODO(jackkleeman): We can also add rotation here when config.HasCertCallback() is true
129- var cancel context.CancelFunc
130122 if config .TLS .ReloadTLSFiles && tlsConfig != nil && tlsConfig .GetClientCertificate != nil {
131123 // The TLS cache is a singleton, so sharing the same name for all of its
132124 // background activity seems okay.
133125 logger := klog .Background ().WithName ("tls-transport-cache" )
134126 dynamicCertDialer := certRotatingDialer (logger , tlsConfig .GetClientCertificate , dial )
135127 tlsConfig .GetClientCertificate = dynamicCertDialer .GetClientCertificate
136128 dial = dynamicCertDialer .connDialer .DialContext
137- var ctx context.Context
138- ctx , cancel = context .WithCancel (context .Background ())
139- go dynamicCertDialer .run (ctx .Done ())
129+ go dynamicCertDialer .run (DialerStopCh )
140130 }
141131
142132 proxy := http .ProxyFromEnvironment
@@ -158,95 +148,12 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
158148 transport = newAtomicTransportHolder (config .TLS .CAFile , config .TLS .CAData , httpTransport )
159149 }
160150
161- if ! canCache && cancel == nil {
162- return transport , nil // uncacheable config with no cert rotation - nothing to GC
163- }
164-
165- if ! clientgofeaturegate .FeatureGates ().Enabled (clientgofeaturegate .ClientsAllowTLSCacheGC ) {
166- if canCache {
167- c .strongTransports [key ] = transport
168- }
169- return transport , nil // cancel is intentionally discarded and the cert rotation go routine leaks
170- }
171-
172- transportWithGC := & trackedTransport {rt : transport }
173-
174- if cancel != nil {
175- // capture metric as local var so that cleanups do not influence other tests via globals
176- transportCertRotationGCCalls := metrics .TransportCertRotationGCCalls
177- runtime .AddCleanup (transportWithGC , func (_ struct {}) {
178- cancel ()
179- transportCertRotationGCCalls .Increment ()
180- }, struct {}{})
181- }
182-
183151 if canCache {
184- wp := weak .Make (transportWithGC )
185- c .transports [key ] = wp
186- // capture metrics as local vars so that cleanups do not influence other tests via globals
187- transportCacheGCCalls := metrics .TransportCacheGCCalls
188- transportCacheEntries := metrics .TransportCacheEntries
189- runtime .AddCleanup (transportWithGC , func (key tlsCacheKey ) {
190- c .mu .Lock ()
191- defer c .mu .Unlock ()
192-
193- // make sure we only delete the weak pointer created by this specific setLocked call
194- if c .transports [key ] != wp {
195- transportCacheGCCalls .Increment ("skipped" )
196- return
197- }
198- delete (c .transports , key )
199- transportCacheGCCalls .Increment ("deleted" )
200- transportCacheEntries .Observe (c .lenLocked ())
201- }, key )
202- }
203-
204- return transportWithGC , nil
205- }
206-
207- func (c * tlsTransportCache ) getLocked (key tlsCacheKey ) (http.RoundTripper , bool ) {
208- if ! clientgofeaturegate .FeatureGates ().Enabled (clientgofeaturegate .ClientsAllowTLSCacheGC ) {
209- v , ok := c .strongTransports [key ]
210- return v , ok
152+ // Cache a single transport for these options
153+ c .transports [key ] = transport
211154 }
212155
213- wp , ok := c .transports [key ]
214- if ! ok {
215- return nil , false
216- }
217-
218- v := wp .Value ()
219-
220- if v == nil { // avoid typed nil
221- return nil , true // key exists but value has been garbage collected
222- }
223-
224- return v , true
225- }
226-
227- func (c * tlsTransportCache ) lenLocked () int {
228- if ! clientgofeaturegate .FeatureGates ().Enabled (clientgofeaturegate .ClientsAllowTLSCacheGC ) {
229- return len (c .strongTransports )
230- }
231- return len (c .transports )
232- }
233-
234- // trackedTransport wraps an http.RoundTripper to serve as the weak.Pointer
235- // target in the TLS transport cache. Dropping all references to this object
236- // triggers GC cleanup of the cache entry and any cert rotation goroutine.
237- type trackedTransport struct {
238- rt http.RoundTripper
239- }
240-
241- var _ http.RoundTripper = & trackedTransport {}
242- var _ utilnet.RoundTripperWrapper = & trackedTransport {}
243-
244- func (v * trackedTransport ) RoundTrip (req * http.Request ) (* http.Response , error ) {
245- return v .rt .RoundTrip (req )
246- }
247-
248- func (v * trackedTransport ) WrappedRoundTripper () http.RoundTripper {
249- return v .rt
156+ return transport , nil
250157}
251158
252159// tlsConfigKey returns a unique key for tls.Config objects returned from TLSConfigFor
0 commit comments