config.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package restclient
  14. import (
  15. "fmt"
  16. "io/ioutil"
  17. "net"
  18. "net/http"
  19. "os"
  20. "path"
  21. gruntime "runtime"
  22. "strings"
  23. "github.com/golang/glog"
  24. "k8s.io/kubernetes/pkg/api"
  25. "k8s.io/kubernetes/pkg/api/unversioned"
  26. clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
  27. "k8s.io/kubernetes/pkg/runtime"
  28. "k8s.io/kubernetes/pkg/util/crypto"
  29. "k8s.io/kubernetes/pkg/util/flowcontrol"
  30. "k8s.io/kubernetes/pkg/version"
  31. )
  32. const (
  33. DefaultQPS float32 = 5.0
  34. DefaultBurst int = 10
  35. )
  36. // Config holds the common attributes that can be passed to a Kubernetes client on
  37. // initialization.
  38. type Config struct {
  39. // Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
  40. // If a URL is given then the (optional) Path of that URL represents a prefix that must
  41. // be appended to all request URIs used to access the apiserver. This allows a frontend
  42. // proxy to easily relocate all of the apiserver endpoints.
  43. Host string
  44. // APIPath is a sub-path that points to an API root.
  45. APIPath string
  46. // Prefix is the sub path of the server. If not specified, the client will set
  47. // a default value. Use "/" to indicate the server root should be used
  48. Prefix string
  49. // ContentConfig contains settings that affect how objects are transformed when
  50. // sent to the server.
  51. ContentConfig
  52. // Server requires Basic authentication
  53. Username string
  54. Password string
  55. // Server requires Bearer authentication. This client will not attempt to use
  56. // refresh tokens for an OAuth2 flow.
  57. // TODO: demonstrate an OAuth2 compatible client.
  58. BearerToken string
  59. // Impersonate is the username that this RESTClient will impersonate
  60. Impersonate string
  61. // Server requires plugin-specified authentication.
  62. AuthProvider *clientcmdapi.AuthProviderConfig
  63. // Callback to persist config for AuthProvider.
  64. AuthConfigPersister AuthProviderConfigPersister
  65. // TLSClientConfig contains settings to enable transport layer security
  66. TLSClientConfig
  67. // Server should be accessed without verifying the TLS
  68. // certificate. For testing only.
  69. Insecure bool
  70. // UserAgent is an optional field that specifies the caller of this request.
  71. UserAgent string
  72. // Transport may be used for custom HTTP behavior. This attribute may not
  73. // be specified with the TLS client certificate options. Use WrapTransport
  74. // for most client level operations.
  75. Transport http.RoundTripper
  76. // WrapTransport will be invoked for custom HTTP behavior after the underlying
  77. // transport is initialized (either the transport created from TLSClientConfig,
  78. // Transport, or http.DefaultTransport). The config may layer other RoundTrippers
  79. // on top of the returned RoundTripper.
  80. WrapTransport func(rt http.RoundTripper) http.RoundTripper
  81. // QPS indicates the maximum QPS to the master from this client.
  82. // If it's zero, the created RESTClient will use DefaultQPS: 5
  83. QPS float32
  84. // Maximum burst for throttle.
  85. // If it's zero, the created RESTClient will use DefaultBurst: 10.
  86. Burst int
  87. // Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
  88. RateLimiter flowcontrol.RateLimiter
  89. // Version forces a specific version to be used (if registered)
  90. // Do we need this?
  91. // Version string
  92. }
  93. // TLSClientConfig contains settings to enable transport layer security
  94. type TLSClientConfig struct {
  95. // Server requires TLS client certificate authentication
  96. CertFile string
  97. // Server requires TLS client certificate authentication
  98. KeyFile string
  99. // Trusted root certificates for server
  100. CAFile string
  101. // CertData holds PEM-encoded bytes (typically read from a client certificate file).
  102. // CertData takes precedence over CertFile
  103. CertData []byte
  104. // KeyData holds PEM-encoded bytes (typically read from a client certificate key file).
  105. // KeyData takes precedence over KeyFile
  106. KeyData []byte
  107. // CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
  108. // CAData takes precedence over CAFile
  109. CAData []byte
  110. }
  111. type ContentConfig struct {
  112. // AcceptContentTypes specifies the types the client will accept and is optional.
  113. // If not set, ContentType will be used to define the Accept header
  114. AcceptContentTypes string
  115. // ContentType specifies the wire format used to communicate with the server.
  116. // This value will be set as the Accept header on requests made to the server, and
  117. // as the default content type on any object sent to the server. If not set,
  118. // "application/json" is used.
  119. ContentType string
  120. // GroupVersion is the API version to talk to. Must be provided when initializing
  121. // a RESTClient directly. When initializing a Client, will be set with the default
  122. // code version.
  123. GroupVersion *unversioned.GroupVersion
  124. // NegotiatedSerializer is used for obtaining encoders and decoders for multiple
  125. // supported media types.
  126. NegotiatedSerializer runtime.NegotiatedSerializer
  127. }
  128. // RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
  129. // object. Note that a RESTClient may require fields that are optional when initializing a Client.
  130. // A RESTClient created by this method is generic - it expects to operate on an API that follows
  131. // the Kubernetes conventions, but may not be the Kubernetes API.
  132. func RESTClientFor(config *Config) (*RESTClient, error) {
  133. if config.GroupVersion == nil {
  134. return nil, fmt.Errorf("GroupVersion is required when initializing a RESTClient")
  135. }
  136. if config.NegotiatedSerializer == nil {
  137. return nil, fmt.Errorf("NegotiatedSerializer is required when initializing a RESTClient")
  138. }
  139. qps := config.QPS
  140. if config.QPS == 0.0 {
  141. qps = DefaultQPS
  142. }
  143. burst := config.Burst
  144. if config.Burst == 0 {
  145. burst = DefaultBurst
  146. }
  147. baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
  148. if err != nil {
  149. return nil, err
  150. }
  151. transport, err := TransportFor(config)
  152. if err != nil {
  153. return nil, err
  154. }
  155. var httpClient *http.Client
  156. if transport != http.DefaultTransport {
  157. httpClient = &http.Client{Transport: transport}
  158. }
  159. return NewRESTClient(baseURL, versionedAPIPath, config.ContentConfig, qps, burst, config.RateLimiter, httpClient)
  160. }
  161. // UnversionedRESTClientFor is the same as RESTClientFor, except that it allows
  162. // the config.Version to be empty.
  163. func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
  164. if config.NegotiatedSerializer == nil {
  165. return nil, fmt.Errorf("NeogitatedSerializer is required when initializing a RESTClient")
  166. }
  167. baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
  168. if err != nil {
  169. return nil, err
  170. }
  171. transport, err := TransportFor(config)
  172. if err != nil {
  173. return nil, err
  174. }
  175. var httpClient *http.Client
  176. if transport != http.DefaultTransport {
  177. httpClient = &http.Client{Transport: transport}
  178. }
  179. versionConfig := config.ContentConfig
  180. if versionConfig.GroupVersion == nil {
  181. v := unversioned.SchemeGroupVersion
  182. versionConfig.GroupVersion = &v
  183. }
  184. return NewRESTClient(baseURL, versionedAPIPath, versionConfig, config.QPS, config.Burst, config.RateLimiter, httpClient)
  185. }
  186. // SetKubernetesDefaults sets default values on the provided client config for accessing the
  187. // Kubernetes API or returns an error if any of the defaults are impossible or invalid.
  188. func SetKubernetesDefaults(config *Config) error {
  189. if len(config.UserAgent) == 0 {
  190. config.UserAgent = DefaultKubernetesUserAgent()
  191. }
  192. return nil
  193. }
  194. // DefaultKubernetesUserAgent returns the default user agent that clients can use.
  195. func DefaultKubernetesUserAgent() string {
  196. commit := version.Get().GitCommit
  197. if len(commit) > 7 {
  198. commit = commit[:7]
  199. }
  200. if len(commit) == 0 {
  201. commit = "unknown"
  202. }
  203. version := version.Get().GitVersion
  204. seg := strings.SplitN(version, "-", 2)
  205. version = seg[0]
  206. return fmt.Sprintf("%s/%s (%s/%s) kubernetes/%s", path.Base(os.Args[0]), version, gruntime.GOOS, gruntime.GOARCH, commit)
  207. }
  208. // InClusterConfig returns a config object which uses the service account
  209. // kubernetes gives to pods. It's intended for clients that expect to be
  210. // running inside a pod running on kubernetes. It will return an error if
  211. // called from a process not running in a kubernetes environment.
  212. func InClusterConfig() (*Config, error) {
  213. host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
  214. if len(host) == 0 || len(port) == 0 {
  215. return nil, fmt.Errorf("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
  216. }
  217. token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountTokenKey)
  218. if err != nil {
  219. return nil, err
  220. }
  221. tlsClientConfig := TLSClientConfig{}
  222. rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountRootCAKey
  223. if _, err := crypto.CertPoolFromFile(rootCAFile); err != nil {
  224. glog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
  225. } else {
  226. tlsClientConfig.CAFile = rootCAFile
  227. }
  228. return &Config{
  229. // TODO: switch to using cluster DNS.
  230. Host: "https://" + net.JoinHostPort(host, port),
  231. BearerToken: string(token),
  232. TLSClientConfig: tlsClientConfig,
  233. }, nil
  234. }
  235. // IsConfigTransportTLS returns true if and only if the provided
  236. // config will result in a protected connection to the server when it
  237. // is passed to restclient.RESTClientFor(). Use to determine when to
  238. // send credentials over the wire.
  239. //
  240. // Note: the Insecure flag is ignored when testing for this value, so MITM attacks are
  241. // still possible.
  242. func IsConfigTransportTLS(config Config) bool {
  243. baseURL, _, err := defaultServerUrlFor(&config)
  244. if err != nil {
  245. return false
  246. }
  247. return baseURL.Scheme == "https"
  248. }
  249. // LoadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
  250. // KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
  251. // either populated or were empty to start.
  252. func LoadTLSFiles(c *Config) error {
  253. var err error
  254. c.CAData, err = dataFromSliceOrFile(c.CAData, c.CAFile)
  255. if err != nil {
  256. return err
  257. }
  258. c.CertData, err = dataFromSliceOrFile(c.CertData, c.CertFile)
  259. if err != nil {
  260. return err
  261. }
  262. c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
  263. if err != nil {
  264. return err
  265. }
  266. return nil
  267. }
  268. // dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
  269. // or an error if an error occurred reading the file
  270. func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
  271. if len(data) > 0 {
  272. return data, nil
  273. }
  274. if len(file) > 0 {
  275. fileData, err := ioutil.ReadFile(file)
  276. if err != nil {
  277. return []byte{}, err
  278. }
  279. return fileData, nil
  280. }
  281. return nil, nil
  282. }
  283. func AddUserAgent(config *Config, userAgent string) *Config {
  284. fullUserAgent := DefaultKubernetesUserAgent() + "/" + userAgent
  285. config.UserAgent = fullUserAgent
  286. return config
  287. }