Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / k8s.io / client-go / rest / config.go
1 /*
2 Copyright 2016 The Kubernetes Authors.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 package rest
18
19 import (
20         "context"
21         "errors"
22         "fmt"
23         "io/ioutil"
24         "net"
25         "net/http"
26         "os"
27         "path/filepath"
28         gruntime "runtime"
29         "strings"
30         "time"
31
32         metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33         "k8s.io/apimachinery/pkg/runtime"
34         "k8s.io/apimachinery/pkg/runtime/schema"
35         "k8s.io/client-go/pkg/version"
36         clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
37         certutil "k8s.io/client-go/util/cert"
38         "k8s.io/client-go/util/flowcontrol"
39         "k8s.io/klog"
40 )
41
42 const (
43         DefaultQPS   float32 = 5.0
44         DefaultBurst int     = 10
45 )
46
47 var ErrNotInCluster = errors.New("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
48
49 // Config holds the common attributes that can be passed to a Kubernetes client on
50 // initialization.
51 type Config struct {
52         // Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
53         // If a URL is given then the (optional) Path of that URL represents a prefix that must
54         // be appended to all request URIs used to access the apiserver. This allows a frontend
55         // proxy to easily relocate all of the apiserver endpoints.
56         Host string
57         // APIPath is a sub-path that points to an API root.
58         APIPath string
59
60         // ContentConfig contains settings that affect how objects are transformed when
61         // sent to the server.
62         ContentConfig
63
64         // Server requires Basic authentication
65         Username string
66         Password string
67
68         // Server requires Bearer authentication. This client will not attempt to use
69         // refresh tokens for an OAuth2 flow.
70         // TODO: demonstrate an OAuth2 compatible client.
71         BearerToken string
72
73         // Path to a file containing a BearerToken.
74         // If set, the contents are periodically read.
75         // The last successfully read value takes precedence over BearerToken.
76         BearerTokenFile string
77
78         // Impersonate is the configuration that RESTClient will use for impersonation.
79         Impersonate ImpersonationConfig
80
81         // Server requires plugin-specified authentication.
82         AuthProvider *clientcmdapi.AuthProviderConfig
83
84         // Callback to persist config for AuthProvider.
85         AuthConfigPersister AuthProviderConfigPersister
86
87         // Exec-based authentication provider.
88         ExecProvider *clientcmdapi.ExecConfig
89
90         // TLSClientConfig contains settings to enable transport layer security
91         TLSClientConfig
92
93         // UserAgent is an optional field that specifies the caller of this request.
94         UserAgent string
95
96         // Transport may be used for custom HTTP behavior. This attribute may not
97         // be specified with the TLS client certificate options. Use WrapTransport
98         // for most client level operations.
99         Transport http.RoundTripper
100         // WrapTransport will be invoked for custom HTTP behavior after the underlying
101         // transport is initialized (either the transport created from TLSClientConfig,
102         // Transport, or http.DefaultTransport). The config may layer other RoundTrippers
103         // on top of the returned RoundTripper.
104         WrapTransport func(rt http.RoundTripper) http.RoundTripper
105
106         // QPS indicates the maximum QPS to the master from this client.
107         // If it's zero, the created RESTClient will use DefaultQPS: 5
108         QPS float32
109
110         // Maximum burst for throttle.
111         // If it's zero, the created RESTClient will use DefaultBurst: 10.
112         Burst int
113
114         // Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
115         RateLimiter flowcontrol.RateLimiter
116
117         // The maximum length of time to wait before giving up on a server request. A value of zero means no timeout.
118         Timeout time.Duration
119
120         // Dial specifies the dial function for creating unencrypted TCP connections.
121         Dial func(ctx context.Context, network, address string) (net.Conn, error)
122
123         // Version forces a specific version to be used (if registered)
124         // Do we need this?
125         // Version string
126 }
127
128 // ImpersonationConfig has all the available impersonation options
129 type ImpersonationConfig struct {
130         // UserName is the username to impersonate on each request.
131         UserName string
132         // Groups are the groups to impersonate on each request.
133         Groups []string
134         // Extra is a free-form field which can be used to link some authentication information
135         // to authorization information.  This field allows you to impersonate it.
136         Extra map[string][]string
137 }
138
139 // +k8s:deepcopy-gen=true
140 // TLSClientConfig contains settings to enable transport layer security
141 type TLSClientConfig struct {
142         // Server should be accessed without verifying the TLS certificate. For testing only.
143         Insecure bool
144         // ServerName is passed to the server for SNI and is used in the client to check server
145         // ceritificates against. If ServerName is empty, the hostname used to contact the
146         // server is used.
147         ServerName string
148
149         // Server requires TLS client certificate authentication
150         CertFile string
151         // Server requires TLS client certificate authentication
152         KeyFile string
153         // Trusted root certificates for server
154         CAFile string
155
156         // CertData holds PEM-encoded bytes (typically read from a client certificate file).
157         // CertData takes precedence over CertFile
158         CertData []byte
159         // KeyData holds PEM-encoded bytes (typically read from a client certificate key file).
160         // KeyData takes precedence over KeyFile
161         KeyData []byte
162         // CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
163         // CAData takes precedence over CAFile
164         CAData []byte
165 }
166
167 type ContentConfig struct {
168         // AcceptContentTypes specifies the types the client will accept and is optional.
169         // If not set, ContentType will be used to define the Accept header
170         AcceptContentTypes string
171         // ContentType specifies the wire format used to communicate with the server.
172         // This value will be set as the Accept header on requests made to the server, and
173         // as the default content type on any object sent to the server. If not set,
174         // "application/json" is used.
175         ContentType string
176         // GroupVersion is the API version to talk to. Must be provided when initializing
177         // a RESTClient directly. When initializing a Client, will be set with the default
178         // code version.
179         GroupVersion *schema.GroupVersion
180         // NegotiatedSerializer is used for obtaining encoders and decoders for multiple
181         // supported media types.
182         NegotiatedSerializer runtime.NegotiatedSerializer
183 }
184
185 // RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
186 // object. Note that a RESTClient may require fields that are optional when initializing a Client.
187 // A RESTClient created by this method is generic - it expects to operate on an API that follows
188 // the Kubernetes conventions, but may not be the Kubernetes API.
189 func RESTClientFor(config *Config) (*RESTClient, error) {
190         if config.GroupVersion == nil {
191                 return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
192         }
193         if config.NegotiatedSerializer == nil {
194                 return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
195         }
196         qps := config.QPS
197         if config.QPS == 0.0 {
198                 qps = DefaultQPS
199         }
200         burst := config.Burst
201         if config.Burst == 0 {
202                 burst = DefaultBurst
203         }
204
205         baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
206         if err != nil {
207                 return nil, err
208         }
209
210         transport, err := TransportFor(config)
211         if err != nil {
212                 return nil, err
213         }
214
215         var httpClient *http.Client
216         if transport != http.DefaultTransport {
217                 httpClient = &http.Client{Transport: transport}
218                 if config.Timeout > 0 {
219                         httpClient.Timeout = config.Timeout
220                 }
221         }
222
223         return NewRESTClient(baseURL, versionedAPIPath, config.ContentConfig, qps, burst, config.RateLimiter, httpClient)
224 }
225
226 // UnversionedRESTClientFor is the same as RESTClientFor, except that it allows
227 // the config.Version to be empty.
228 func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
229         if config.NegotiatedSerializer == nil {
230                 return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
231         }
232
233         baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
234         if err != nil {
235                 return nil, err
236         }
237
238         transport, err := TransportFor(config)
239         if err != nil {
240                 return nil, err
241         }
242
243         var httpClient *http.Client
244         if transport != http.DefaultTransport {
245                 httpClient = &http.Client{Transport: transport}
246                 if config.Timeout > 0 {
247                         httpClient.Timeout = config.Timeout
248                 }
249         }
250
251         versionConfig := config.ContentConfig
252         if versionConfig.GroupVersion == nil {
253                 v := metav1.SchemeGroupVersion
254                 versionConfig.GroupVersion = &v
255         }
256
257         return NewRESTClient(baseURL, versionedAPIPath, versionConfig, config.QPS, config.Burst, config.RateLimiter, httpClient)
258 }
259
260 // SetKubernetesDefaults sets default values on the provided client config for accessing the
261 // Kubernetes API or returns an error if any of the defaults are impossible or invalid.
262 func SetKubernetesDefaults(config *Config) error {
263         if len(config.UserAgent) == 0 {
264                 config.UserAgent = DefaultKubernetesUserAgent()
265         }
266         return nil
267 }
268
269 // adjustCommit returns sufficient significant figures of the commit's git hash.
270 func adjustCommit(c string) string {
271         if len(c) == 0 {
272                 return "unknown"
273         }
274         if len(c) > 7 {
275                 return c[:7]
276         }
277         return c
278 }
279
280 // adjustVersion strips "alpha", "beta", etc. from version in form
281 // major.minor.patch-[alpha|beta|etc].
282 func adjustVersion(v string) string {
283         if len(v) == 0 {
284                 return "unknown"
285         }
286         seg := strings.SplitN(v, "-", 2)
287         return seg[0]
288 }
289
290 // adjustCommand returns the last component of the
291 // OS-specific command path for use in User-Agent.
292 func adjustCommand(p string) string {
293         // Unlikely, but better than returning "".
294         if len(p) == 0 {
295                 return "unknown"
296         }
297         return filepath.Base(p)
298 }
299
300 // buildUserAgent builds a User-Agent string from given args.
301 func buildUserAgent(command, version, os, arch, commit string) string {
302         return fmt.Sprintf(
303                 "%s/%s (%s/%s) kubernetes/%s", command, version, os, arch, commit)
304 }
305
306 // DefaultKubernetesUserAgent returns a User-Agent string built from static global vars.
307 func DefaultKubernetesUserAgent() string {
308         return buildUserAgent(
309                 adjustCommand(os.Args[0]),
310                 adjustVersion(version.Get().GitVersion),
311                 gruntime.GOOS,
312                 gruntime.GOARCH,
313                 adjustCommit(version.Get().GitCommit))
314 }
315
316 // InClusterConfig returns a config object which uses the service account
317 // kubernetes gives to pods. It's intended for clients that expect to be
318 // running inside a pod running on kubernetes. It will return ErrNotInCluster
319 // if called from a process not running in a kubernetes environment.
320 func InClusterConfig() (*Config, error) {
321         const (
322                 tokenFile  = "/var/run/secrets/kubernetes.io/serviceaccount/token"
323                 rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
324         )
325         host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
326         if len(host) == 0 || len(port) == 0 {
327                 return nil, ErrNotInCluster
328         }
329
330         token, err := ioutil.ReadFile(tokenFile)
331         if err != nil {
332                 return nil, err
333         }
334
335         tlsClientConfig := TLSClientConfig{}
336
337         if _, err := certutil.NewPool(rootCAFile); err != nil {
338                 klog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
339         } else {
340                 tlsClientConfig.CAFile = rootCAFile
341         }
342
343         return &Config{
344                 // TODO: switch to using cluster DNS.
345                 Host:            "https://" + net.JoinHostPort(host, port),
346                 TLSClientConfig: tlsClientConfig,
347                 BearerToken:     string(token),
348                 BearerTokenFile: tokenFile,
349         }, nil
350 }
351
352 // IsConfigTransportTLS returns true if and only if the provided
353 // config will result in a protected connection to the server when it
354 // is passed to restclient.RESTClientFor().  Use to determine when to
355 // send credentials over the wire.
356 //
357 // Note: the Insecure flag is ignored when testing for this value, so MITM attacks are
358 // still possible.
359 func IsConfigTransportTLS(config Config) bool {
360         baseURL, _, err := defaultServerUrlFor(&config)
361         if err != nil {
362                 return false
363         }
364         return baseURL.Scheme == "https"
365 }
366
367 // LoadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
368 // KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
369 // either populated or were empty to start.
370 func LoadTLSFiles(c *Config) error {
371         var err error
372         c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile)
373         if err != nil {
374                 return err
375         }
376
377         c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile)
378         if err != nil {
379                 return err
380         }
381
382         c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
383         if err != nil {
384                 return err
385         }
386         return nil
387 }
388
389 // dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
390 // or an error if an error occurred reading the file
391 func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
392         if len(data) > 0 {
393                 return data, nil
394         }
395         if len(file) > 0 {
396                 fileData, err := ioutil.ReadFile(file)
397                 if err != nil {
398                         return []byte{}, err
399                 }
400                 return fileData, nil
401         }
402         return nil, nil
403 }
404
405 func AddUserAgent(config *Config, userAgent string) *Config {
406         fullUserAgent := DefaultKubernetesUserAgent() + "/" + userAgent
407         config.UserAgent = fullUserAgent
408         return config
409 }
410
411 // AnonymousClientConfig returns a copy of the given config with all user credentials (cert/key, bearer token, and username/password) removed
412 func AnonymousClientConfig(config *Config) *Config {
413         // copy only known safe fields
414         return &Config{
415                 Host:          config.Host,
416                 APIPath:       config.APIPath,
417                 ContentConfig: config.ContentConfig,
418                 TLSClientConfig: TLSClientConfig{
419                         Insecure:   config.Insecure,
420                         ServerName: config.ServerName,
421                         CAFile:     config.TLSClientConfig.CAFile,
422                         CAData:     config.TLSClientConfig.CAData,
423                 },
424                 RateLimiter:   config.RateLimiter,
425                 UserAgent:     config.UserAgent,
426                 Transport:     config.Transport,
427                 WrapTransport: config.WrapTransport,
428                 QPS:           config.QPS,
429                 Burst:         config.Burst,
430                 Timeout:       config.Timeout,
431                 Dial:          config.Dial,
432         }
433 }
434
435 // CopyConfig returns a copy of the given config
436 func CopyConfig(config *Config) *Config {
437         return &Config{
438                 Host:            config.Host,
439                 APIPath:         config.APIPath,
440                 ContentConfig:   config.ContentConfig,
441                 Username:        config.Username,
442                 Password:        config.Password,
443                 BearerToken:     config.BearerToken,
444                 BearerTokenFile: config.BearerTokenFile,
445                 Impersonate: ImpersonationConfig{
446                         Groups:   config.Impersonate.Groups,
447                         Extra:    config.Impersonate.Extra,
448                         UserName: config.Impersonate.UserName,
449                 },
450                 AuthProvider:        config.AuthProvider,
451                 AuthConfigPersister: config.AuthConfigPersister,
452                 ExecProvider:        config.ExecProvider,
453                 TLSClientConfig: TLSClientConfig{
454                         Insecure:   config.TLSClientConfig.Insecure,
455                         ServerName: config.TLSClientConfig.ServerName,
456                         CertFile:   config.TLSClientConfig.CertFile,
457                         KeyFile:    config.TLSClientConfig.KeyFile,
458                         CAFile:     config.TLSClientConfig.CAFile,
459                         CertData:   config.TLSClientConfig.CertData,
460                         KeyData:    config.TLSClientConfig.KeyData,
461                         CAData:     config.TLSClientConfig.CAData,
462                 },
463                 UserAgent:     config.UserAgent,
464                 Transport:     config.Transport,
465                 WrapTransport: config.WrapTransport,
466                 QPS:           config.QPS,
467                 Burst:         config.Burst,
468                 RateLimiter:   config.RateLimiter,
469                 Timeout:       config.Timeout,
470                 Dial:          config.Dial,
471         }
472 }