Skip to content

Commit d5ec191

Browse files
committed
make *http.Client configurable
Signed-off-by: Tim Ramlot <[email protected]>
1 parent b756161 commit d5ec191

File tree

9 files changed

+158
-26
lines changed

9 files changed

+158
-26
lines changed

pkg/cache/cache.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package cache
1919
import (
2020
"context"
2121
"fmt"
22+
"net/http"
2223
"reflect"
2324
"time"
2425

@@ -158,6 +159,15 @@ var defaultResyncTime = 10 * time.Hour
158159

159160
// New initializes and returns a new Cache.
160161
func New(config *rest.Config, opts Options) (Cache, error) {
162+
httpClient, err := rest.HTTPClientFor(config)
163+
if err != nil {
164+
return nil, err
165+
}
166+
return NewForConfigAndClient(config, httpClient, opts)
167+
}
168+
169+
// NewForConfigAndClient initializes and returns a new Cache.
170+
func NewForConfigAndClient(config *rest.Config, httpClient *http.Client, opts Options) (Cache, error) {
161171
opts, err := defaultOpts(config, opts)
162172
if err != nil {
163173
return nil, err
@@ -183,7 +193,7 @@ func New(config *rest.Config, opts Options) (Cache, error) {
183193

184194
return &informerCache{
185195
scheme: opts.Scheme,
186-
InformersMap: internal.NewInformersMap(config, &internal.InformersMapOptions{
196+
InformersMap: internal.NewInformersMapForConfigAndClient(config, httpClient, &internal.InformersMapOptions{
187197
Scheme: opts.Scheme,
188198
Mapper: opts.Mapper,
189199
ResyncPeriod: *opts.Resync,

pkg/cache/internal/informers_map.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"math/rand"
23+
"net/http"
2324
"sync"
2425
"time"
2526

@@ -60,11 +61,33 @@ type InformersMapOptionsByGVK struct {
6061
}
6162

6263
// NewInformersMap creates a new InformersMap that can create informers under the hood.
64+
// Deprecated: use NewInformersMapForConfig instead.
6365
func NewInformersMap(config *rest.Config, options *InformersMapOptions) *InformersMap {
66+
informersMap, err := NewInformersMapForConfig(config, options)
67+
if err != nil {
68+
panic(fmt.Sprintf("unable to create informers map: %v", err))
69+
}
70+
return informersMap
71+
}
72+
73+
// NewInformersMapForConfig creates a new InformersMap that can create informers under the hood.
74+
// It differs from NewInformersMap in that allows you to pass in a custom http.Client.
75+
func NewInformersMapForConfig(config *rest.Config, options *InformersMapOptions) (*InformersMap, error) {
76+
httpClient, err := rest.HTTPClientFor(config)
77+
if err != nil {
78+
return nil, err
79+
}
80+
return NewInformersMapForConfigAndClient(config, httpClient, options), nil
81+
}
82+
83+
// NewInformersMapForConfigAndClient creates a new InformersMap that can create informers under the hood.
84+
// It differs from NewInformersMapForConfig in that it allows you to pass in a custom http.Client.
85+
func NewInformersMapForConfigAndClient(config *rest.Config, h *http.Client, options *InformersMapOptions) *InformersMap {
6486
return &InformersMap{
65-
config: config,
66-
scheme: options.Scheme,
67-
mapper: options.Mapper,
87+
httpClient: h,
88+
config: config,
89+
scheme: options.Scheme,
90+
mapper: options.Mapper,
6891
informers: informers{
6992
Structured: make(map[schema.GroupVersionKind]*MapEntry),
7093
Unstructured: make(map[schema.GroupVersionKind]*MapEntry),
@@ -99,6 +122,9 @@ type informers struct {
99122
// InformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs.
100123
// It uses a standard parameter codec constructed based on the given generated Scheme.
101124
type InformersMap struct {
125+
// httpClient is used to create a new REST client
126+
httpClient *http.Client
127+
102128
// scheme maps runtime.Objects to GroupVersionKinds
103129
scheme *runtime.Scheme
104130

@@ -343,7 +369,7 @@ func (ip *InformersMap) makeListWatcher(gvk schema.GroupVersionKind, obj runtime
343369
// we should remove it and use the one that the dynamic client sets for us.
344370
cfg := rest.CopyConfig(ip.config)
345371
cfg.NegotiatedSerializer = nil
346-
dynamicClient, err := dynamic.NewForConfig(cfg)
372+
dynamicClient, err := dynamic.NewForConfigAndClient(cfg, ip.httpClient)
347373
if err != nil {
348374
return nil, err
349375
}
@@ -373,7 +399,7 @@ func (ip *InformersMap) makeListWatcher(gvk schema.GroupVersionKind, obj runtime
373399
cfg.NegotiatedSerializer = nil
374400

375401
// Grab the metadata metadataClient.
376-
metadataClient, err := metadata.NewForConfig(cfg)
402+
metadataClient, err := metadata.NewForConfigAndClient(cfg, ip.httpClient)
377403
if err != nil {
378404
return nil, err
379405
}
@@ -414,7 +440,7 @@ func (ip *InformersMap) makeListWatcher(gvk schema.GroupVersionKind, obj runtime
414440
// Structured.
415441
//
416442
default:
417-
client, err := apiutil.RESTClientForGVK(gvk, false, ip.config, ip.codecs)
443+
client, err := apiutil.RESTClientForGVKAndClient(gvk, false, ip.config, ip.codecs, ip.httpClient)
418444
if err != nil {
419445
return nil, err
420446
}

pkg/client/apiutil/apimachinery.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package apiutil
2121

2222
import (
2323
"fmt"
24+
"net/http"
2425
"reflect"
2526
"sync"
2627

@@ -122,6 +123,14 @@ func RESTClientForGVK(gvk schema.GroupVersionKind, isUnstructured bool, baseConf
122123
return rest.RESTClientFor(createRestConfig(gvk, isUnstructured, baseConfig, codecs))
123124
}
124125

126+
// RESTClientForGVKAndClient constructs a new rest.Interface capable of accessing the resource associated
127+
// with the given GroupVersionKind. The REST client will be configured to use the negotiated serializer from
128+
// baseConfig, if set, otherwise a default serializer will be set.
129+
// Unlike RESTClientForGVK, this function allows the caller to specify a custom http.Client.
130+
func RESTClientForGVKAndClient(gvk schema.GroupVersionKind, isUnstructured bool, baseConfig *rest.Config, codecs serializer.CodecFactory, httpClient *http.Client) (rest.Interface, error) {
131+
return rest.RESTClientForConfigAndClient(createRestConfig(gvk, isUnstructured, baseConfig, codecs), httpClient)
132+
}
133+
125134
// serializerWithDecodedGVK is a CodecFactory that overrides the DecoderToVersion of a WithoutConversionCodecFactory
126135
// in order to avoid clearing the GVK from the decoded object.
127136
//

pkg/client/apiutil/dynamicrestmapper.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package apiutil
1818

1919
import (
20+
"net/http"
2021
"sync"
2122
"sync/atomic"
2223

@@ -76,7 +77,19 @@ func WithCustomMapper(newMapper func() (meta.RESTMapper, error)) DynamicRESTMapp
7677
// RESTMapper dynamically discovers resource types at runtime. opts
7778
// configure the RESTMapper.
7879
func NewDynamicRESTMapper(cfg *rest.Config, opts ...DynamicRESTMapperOption) (meta.RESTMapper, error) {
79-
client, err := discovery.NewDiscoveryClientForConfig(cfg)
80+
httpClient, err := rest.HTTPClientFor(cfg)
81+
if err != nil {
82+
return nil, err
83+
}
84+
return NewDynamicRESTMapperForConfigAndClient(cfg, httpClient, opts...)
85+
}
86+
87+
// NewDynamicRESTMapperForConfigAndClient returns a dynamic RESTMapper for cfg. The dynamic
88+
// RESTMapper dynamically discovers resource types at runtime. opts
89+
// configure the RESTMapper.
90+
// Unlike NewDynamicRESTMapper, this function allows you to specify a custom http client.
91+
func NewDynamicRESTMapperForConfigAndClient(cfg *rest.Config, httpClient *http.Client, opts ...DynamicRESTMapperOption) (meta.RESTMapper, error) {
92+
client, err := discovery.NewDiscoveryClientForConfigAndClient(cfg, httpClient)
8093
if err != nil {
8194
return nil, err
8295
}

pkg/client/client.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
"net/http"
2324
"strings"
2425

2526
"k8s.io/apimachinery/pkg/api/meta"
@@ -74,14 +75,37 @@ type Options struct {
7475
// case of unstructured types, the group, version, and kind will be extracted
7576
// from the corresponding fields on the object.
7677
func New(config *rest.Config, options Options) (Client, error) {
77-
return newClient(config, options)
78+
httpClient, err := rest.HTTPClientFor(config)
79+
if err != nil {
80+
return nil, err
81+
}
82+
return newClient(config, httpClient, options)
7883
}
7984

80-
func newClient(config *rest.Config, options Options) (*client, error) {
85+
// NewForConfigAndClient returns a new Client using the provided config and Options.
86+
// The returned client reads *and* writes directly from the server
87+
// (it doesn't use object caches). It understands how to work with
88+
// normal types (both custom resources and aggregated/built-in resources),
89+
// as well as unstructured types.
90+
//
91+
// In the case of normal types, the scheme will be used to look up the
92+
// corresponding group, version, and kind for the given type. In the
93+
// case of unstructured types, the group, version, and kind will be extracted
94+
// from the corresponding fields on the object.
95+
// Unlike New, this function allows you to provide your own http.Client.
96+
func NewForConfigAndClient(config *rest.Config, httpClient *http.Client, options Options) (Client, error) {
97+
return newClient(config, httpClient, options)
98+
}
99+
100+
func newClient(config *rest.Config, httpClient *http.Client, options Options) (*client, error) {
81101
if config == nil {
82102
return nil, fmt.Errorf("must provide non-nil rest.Config to client.New")
83103
}
84104

105+
if httpClient == nil {
106+
return nil, fmt.Errorf("must provide non-nil http.Client to client.NewForConfigAndClient")
107+
}
108+
85109
if !options.Opts.SuppressWarnings {
86110
// surface warnings
87111
logger := log.Log.WithName("KubeAPIWarningLogger")
@@ -113,16 +137,17 @@ func newClient(config *rest.Config, options Options) (*client, error) {
113137
}
114138

115139
resources := &clientRestResources{
116-
config: config,
117-
scheme: options.Scheme,
118-
mapper: options.Mapper,
119-
codecs: serializer.NewCodecFactory(options.Scheme),
140+
httpClient: httpClient,
141+
config: config,
142+
scheme: options.Scheme,
143+
mapper: options.Mapper,
144+
codecs: serializer.NewCodecFactory(options.Scheme),
120145

121146
structuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta),
122147
unstructuredResourceByType: make(map[schema.GroupVersionKind]*resourceMeta),
123148
}
124149

125-
rawMetaClient, err := metadata.NewForConfig(config)
150+
rawMetaClient, err := metadata.NewForConfigAndClient(config, httpClient)
126151
if err != nil {
127152
return nil, fmt.Errorf("unable to construct metadata-only client for use as part of client: %w", err)
128153
}

pkg/client/client_rest_resources.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package client
1818

1919
import (
20+
"net/http"
2021
"strings"
2122
"sync"
2223

@@ -32,6 +33,9 @@ import (
3233

3334
// clientRestResources creates and stores rest clients and metadata for Kubernetes types.
3435
type clientRestResources struct {
36+
// httpClient is the http client to use for requests
37+
httpClient *http.Client
38+
3539
// config is the rest.Config to talk to an apiserver
3640
config *rest.Config
3741

@@ -59,7 +63,7 @@ func (c *clientRestResources) newResource(gvk schema.GroupVersionKind, isList, i
5963
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
6064
}
6165

62-
client, err := apiutil.RESTClientForGVK(gvk, isUnstructured, c.config, c.codecs)
66+
client, err := apiutil.RESTClientForGVKAndClient(gvk, isUnstructured, c.config, c.codecs, c.httpClient)
6367
if err != nil {
6468
return nil, err
6569
}

pkg/client/watch.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package client
1818

1919
import (
2020
"context"
21+
"net/http"
2122
"strings"
2223

2324
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -29,11 +30,21 @@ import (
2930

3031
// NewWithWatch returns a new WithWatch.
3132
func NewWithWatch(config *rest.Config, options Options) (WithWatch, error) {
32-
client, err := newClient(config, options)
33+
httpClient, err := rest.HTTPClientFor(config)
3334
if err != nil {
3435
return nil, err
3536
}
36-
dynamicClient, err := dynamic.NewForConfig(config)
37+
return NewWithWatchForConfigAndClient(config, httpClient, options)
38+
}
39+
40+
// NewWithWatchForConfigAndClient is a client that can watch objects.
41+
// It differs from NewForConfigAndClient in that it returns a client that can watch objects.
42+
func NewWithWatchForConfigAndClient(config *rest.Config, httpClient *http.Client, options Options) (WithWatch, error) {
43+
client, err := newClient(config, httpClient, options)
44+
if err != nil {
45+
return nil, err
46+
}
47+
dynamicClient, err := dynamic.NewForConfigAndClient(config, httpClient)
3748
if err != nil {
3849
return nil, err
3950
}

pkg/cluster/cluster.go

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package cluster
1919
import (
2020
"context"
2121
"errors"
22+
"net/http"
2223
"time"
2324

2425
"github.com/go-logr/logr"
@@ -105,6 +106,11 @@ type Options struct {
105106
// will only hold objects from the desired namespace.
106107
Namespace string
107108

109+
// HTTPClient is the http client that will be used to create the default
110+
// Cache and Client. If not set the rest.HTTPClientFor function will be used
111+
// to create the http client.
112+
HTTPClient *http.Client
113+
108114
// NewCache is the function that will create the cache to be used
109115
// by the manager. If not set this will use the default new cache function.
110116
NewCache cache.NewCacheFunc
@@ -153,7 +159,7 @@ func New(config *rest.Config, opts ...Option) (Cluster, error) {
153159
for _, opt := range opts {
154160
opt(&options)
155161
}
156-
options = setOptionsDefaults(options)
162+
options = setOptionsDefaults(options, config)
157163

158164
// Create the mapper provider
159165
mapper, err := options.MapperProvider(config)
@@ -206,31 +212,47 @@ func New(config *rest.Config, opts ...Option) (Cluster, error) {
206212
}
207213

208214
// setOptionsDefaults set default values for Options fields.
209-
func setOptionsDefaults(options Options) Options {
215+
func setOptionsDefaults(options Options, config *rest.Config) Options {
216+
if options.HTTPClient == nil {
217+
httpClient, err := rest.HTTPClientFor(config)
218+
if err != nil {
219+
panic(err)
220+
}
221+
options.HTTPClient = httpClient
222+
}
223+
210224
// Use the Kubernetes client-go scheme if none is specified
211225
if options.Scheme == nil {
212226
options.Scheme = scheme.Scheme
213227
}
214228

215229
if options.MapperProvider == nil {
216230
options.MapperProvider = func(c *rest.Config) (meta.RESTMapper, error) {
217-
return apiutil.NewDynamicRESTMapper(c)
231+
return apiutil.NewDynamicRESTMapperForConfigAndClient(c, options.HTTPClient)
218232
}
219233
}
220234

221235
// Allow users to define how to create a new client
222236
if options.NewClient == nil {
223-
options.NewClient = DefaultNewClient
237+
options.NewClient = func(cache cache.Cache, config *rest.Config, clientOptions client.Options, uncachedObjects ...client.Object) (client.Client, error) {
238+
return ClientBuilderWithOptions(ClientOptions{
239+
HTTPClient: options.HTTPClient,
240+
})(cache, config, clientOptions, uncachedObjects...)
241+
}
224242
}
225243

226244
// Allow newCache to be mocked
227245
if options.NewCache == nil {
228-
options.NewCache = cache.New
246+
options.NewCache = func(config *rest.Config, opts cache.Options) (cache.Cache, error) {
247+
return cache.NewForConfigAndClient(config, options.HTTPClient, opts)
248+
}
229249
}
230250

231251
// Allow newRecorderProvider to be mocked
232252
if options.newRecorderProvider == nil {
233-
options.newRecorderProvider = intrec.NewProvider
253+
options.newRecorderProvider = func(config *rest.Config, scheme *runtime.Scheme, logger logr.Logger, makeBroadcaster intrec.EventBroadcasterProducer) (*intrec.Provider, error) {
254+
return intrec.NewProviderForConfigAndClient(config, options.HTTPClient, scheme, logger, makeBroadcaster)
255+
}
234256
}
235257

236258
// This is duplicated with pkg/manager, we need it here to provide
@@ -260,6 +282,7 @@ type NewClientFunc func(cache cache.Cache, config *rest.Config, options client.O
260282
type ClientOptions struct {
261283
UncachedObjects []client.Object
262284
CacheUnstructured bool
285+
HTTPClient *http.Client
263286
}
264287

265288
// DefaultNewClient creates the default caching client, that will never cache Unstructured.
@@ -273,7 +296,7 @@ func ClientBuilderWithOptions(options ClientOptions) NewClientFunc {
273296
return func(cache cache.Cache, config *rest.Config, clientOpts client.Options, uncachedObjects ...client.Object) (client.Client, error) {
274297
options.UncachedObjects = append(options.UncachedObjects, uncachedObjects...)
275298

276-
c, err := client.New(config, clientOpts)
299+
c, err := client.NewForConfigAndClient(config, options.HTTPClient, clientOpts)
277300
if err != nil {
278301
return nil, err
279302
}

0 commit comments

Comments
 (0)