2014-09-30 00:15:00 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2014-09-30 00:15:00 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2015-08-12 17:35:07 +00:00
package unversioned
2014-09-30 00:15:00 +00:00
import (
2015-10-01 21:56:22 +00:00
"encoding/json"
2014-09-30 00:15:00 +00:00
"fmt"
2015-05-29 21:31:00 +00:00
"io/ioutil"
2015-02-10 13:19:54 +00:00
"net"
2014-09-30 00:15:00 +00:00
"net/http"
"net/url"
2015-02-04 21:35:53 +00:00
"os"
2014-09-30 00:15:00 +00:00
"path"
2015-01-07 20:59:22 +00:00
"reflect"
2015-02-04 21:35:53 +00:00
gruntime "runtime"
2014-09-30 00:15:00 +00:00
"strings"
2015-07-24 17:55:11 +00:00
"sync"
2015-02-10 13:19:54 +00:00
"time"
2014-09-30 00:15:00 +00:00
2015-08-05 22:05:17 +00:00
"github.com/golang/glog"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/latest"
2015-10-12 20:48:14 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util"
2015-09-09 17:45:01 +00:00
"k8s.io/kubernetes/pkg/util/sets"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/version"
2014-09-30 00:15:00 +00:00
)
// Config holds the common attributes that can be passed to a Kubernetes client on
// initialization.
type Config struct {
// Host must be a host string, a host:port pair, or a URL to the base of the API.
2014-10-03 02:03:52 +00:00
Host string
// Prefix is the sub path of the server. If not specified, the client will set
// a default value. Use "/" to indicate the server root should be used
Prefix string
2015-01-06 17:36:08 +00:00
// Version is the API version to talk to. Must be provided when initializing
// a RESTClient directly. When initializing a Client, will be set with the default
// code version.
2014-09-30 00:15:00 +00:00
Version string
2015-01-06 17:36:08 +00:00
// Codec specifies the encoding and decoding behavior for runtime.Objects passed
// to a RESTClient or Client. Required when initializing a RESTClient, optional
// when initializing a Client.
Codec runtime . Codec
2014-09-30 00:15:00 +00:00
// Server requires Basic authentication
Username string
Password string
// Server requires Bearer authentication. This client will not attempt to use
// refresh tokens for an OAuth2 flow.
// TODO: demonstrate an OAuth2 compatible client.
BearerToken string
2015-01-29 22:43:09 +00:00
// TLSClientConfig contains settings to enable transport layer security
TLSClientConfig
2014-09-30 00:15:00 +00:00
// Server should be accessed without verifying the TLS
// certificate. For testing only.
Insecure bool
2015-02-04 21:35:53 +00:00
// UserAgent is an optional field that specifies the caller of this request.
UserAgent string
2014-09-30 00:15:00 +00:00
// Transport may be used for custom HTTP behavior. This attribute may not
2015-04-11 16:45:30 +00:00
// be specified with the TLS client certificate options. Use WrapTransport
// for most client level operations.
2014-09-30 00:15:00 +00:00
Transport http . RoundTripper
2015-04-11 16:45:30 +00:00
// WrapTransport will be invoked for custom HTTP behavior after the underlying
// transport is initialized (either the transport created from TLSClientConfig,
// Transport, or http.DefaultTransport). The config may layer other RoundTrippers
// on top of the returned RoundTripper.
WrapTransport func ( rt http . RoundTripper ) http . RoundTripper
2015-03-31 03:21:21 +00:00
// QPS indicates the maximum QPS to the master from this client. If zero, QPS is unlimited.
QPS float32
2015-04-09 17:12:52 +00:00
// Maximum burst for throttle
Burst int
2014-09-30 00:15:00 +00:00
}
2014-10-10 22:19:23 +00:00
type KubeletConfig struct {
// ToDo: Add support for different kubelet instances exposing different ports
Port uint
EnableHttps bool
2015-01-29 22:43:09 +00:00
// TLSClientConfig contains settings to enable transport layer security
TLSClientConfig
2015-03-19 01:11:39 +00:00
2015-10-05 19:13:00 +00:00
// Server requires Bearer authentication
BearerToken string
2015-03-19 01:11:39 +00:00
// HTTPTimeout is used by the client to timeout http requests to Kubelet.
HTTPTimeout time . Duration
2015-05-29 22:33:22 +00:00
// Dial is a custom dialer used for the client
Dial func ( net , addr string ) ( net . Conn , error )
2015-01-29 22:43:09 +00:00
}
// TLSClientConfig contains settings to enable transport layer security
type TLSClientConfig struct {
// Server requires TLS client certificate authentication
2014-10-10 22:19:23 +00:00
CertFile string
2015-01-29 22:43:09 +00:00
// Server requires TLS client certificate authentication
2014-10-10 22:19:23 +00:00
KeyFile string
2015-01-29 22:43:09 +00:00
// Trusted root certificates for server
2014-10-10 22:19:23 +00:00
CAFile string
2015-01-16 16:51:53 +00:00
// CertData holds PEM-encoded bytes (typically read from a client certificate file).
// CertData takes precedence over CertFile
CertData [ ] byte
// KeyData holds PEM-encoded bytes (typically read from a client certificate key file).
// KeyData takes precedence over KeyFile
KeyData [ ] byte
// CAData holds PEM-encoded bytes (typically read from a root certificates bundle).
// CAData takes precedence over CAFile
CAData [ ] byte
2014-10-10 22:19:23 +00:00
}
2014-09-30 00:15:00 +00:00
// New creates a Kubernetes client for the given config. This client works with pods,
2015-08-07 06:29:31 +00:00
// replication controllers, daemons, and services. It allows operations such as list, get, update
2014-09-30 00:15:00 +00:00
// and delete on these objects. An error is returned if the provided configuration
// is not valid.
func New ( c * Config ) ( * Client , error ) {
2014-10-03 02:03:52 +00:00
config := * c
2015-01-06 17:36:08 +00:00
if err := SetKubernetesDefaults ( & config ) ; err != nil {
return nil , err
2014-10-03 02:03:52 +00:00
}
client , err := RESTClientFor ( & config )
2014-09-30 00:15:00 +00:00
if err != nil {
return nil , err
}
2015-09-11 22:46:18 +00:00
2015-10-15 23:34:30 +00:00
discoveryConfig := * c
discoveryClient , err := NewDiscoveryClient ( & discoveryConfig )
if err != nil {
return nil , err
}
2015-10-09 22:14:03 +00:00
if _ , err := latest . Group ( "extensions" ) ; err != nil {
2015-10-15 23:34:30 +00:00
return & Client { RESTClient : client , ExtensionsClient : nil , DiscoveryClient : discoveryClient } , nil
2015-09-11 22:46:18 +00:00
}
2015-08-31 16:29:18 +00:00
experimentalConfig := * c
2015-10-12 18:07:31 +00:00
experimentalClient , err := NewExtensions ( & experimentalConfig )
2015-08-31 16:29:18 +00:00
if err != nil {
return nil , err
}
2015-10-15 23:34:30 +00:00
return & Client { RESTClient : client , ExtensionsClient : experimentalClient , DiscoveryClient : discoveryClient } , nil
2014-09-30 00:15:00 +00:00
}
2015-06-13 02:06:18 +00:00
// MatchesServerVersion queries the server to compares the build version
// (git hash) of the client with the server's build version. It returns an error
// if it failed to contact the server or if the versions are not an exact match.
2015-06-17 03:04:51 +00:00
func MatchesServerVersion ( client * Client , c * Config ) error {
var err error
if client == nil {
client , err = New ( c )
if err != nil {
return err
}
2015-01-07 20:59:22 +00:00
}
clientVersion := version . Get ( )
serverVersion , err := client . ServerVersion ( )
if err != nil {
return fmt . Errorf ( "couldn't read version from server: %v\n" , err )
}
if s := * serverVersion ; ! reflect . DeepEqual ( clientVersion , s ) {
return fmt . Errorf ( "server version (%#v) differs from client version (%#v)!\n" , s , clientVersion )
}
return nil
}
2015-10-16 21:36:49 +00:00
func ExtractGroupVersions ( l * unversioned . APIGroupList ) [ ] string {
2015-10-01 21:56:22 +00:00
var groupVersions [ ] string
for _ , g := range l . Groups {
for _ , gv := range g . Versions {
groupVersions = append ( groupVersions , gv . GroupVersion )
}
}
return groupVersions
}
// ServerAPIVersions returns the GroupVersions supported by the API server.
// It creates a RESTClient based on the passed in config, but it doesn't rely
// on the Version, Codec, and Prefix of the config, because it uses AbsPath and
// takes the raw response.
func ServerAPIVersions ( c * Config ) ( groupVersions [ ] string , err error ) {
2015-10-01 23:23:01 +00:00
transport , err := TransportFor ( c )
if err != nil {
return nil , err
}
client := http . Client { Transport : transport }
configCopy := * c
configCopy . Version = ""
configCopy . Prefix = ""
baseURL , err := defaultServerUrlFor ( c )
2015-10-01 21:56:22 +00:00
if err != nil {
return nil , err
}
// Get the groupVersions exposed at /api
2015-10-07 23:19:22 +00:00
baseURL . Path = "/api"
resp , err := client . Get ( baseURL . String ( ) )
2015-10-01 21:56:22 +00:00
if err != nil {
return nil , err
}
2015-10-12 20:48:14 +00:00
var v unversioned . APIVersions
2015-10-07 23:19:22 +00:00
defer resp . Body . Close ( )
err = json . NewDecoder ( resp . Body ) . Decode ( & v )
2015-10-01 21:56:22 +00:00
if err != nil {
2015-10-07 23:19:22 +00:00
return nil , fmt . Errorf ( "unexpected error: %v" , err )
2015-10-01 21:56:22 +00:00
}
2015-10-07 23:19:22 +00:00
2015-10-01 21:56:22 +00:00
groupVersions = append ( groupVersions , v . Versions ... )
// Get the groupVersions exposed at /apis
2015-10-07 23:19:22 +00:00
baseURL . Path = "/apis"
resp2 , err := client . Get ( baseURL . String ( ) )
2015-10-01 21:56:22 +00:00
if err != nil {
return nil , err
}
2015-10-12 20:48:14 +00:00
var apiGroupList unversioned . APIGroupList
2015-10-07 23:19:22 +00:00
defer resp2 . Body . Close ( )
err = json . NewDecoder ( resp2 . Body ) . Decode ( & apiGroupList )
2015-10-01 21:56:22 +00:00
if err != nil {
2015-10-07 23:19:22 +00:00
return nil , fmt . Errorf ( "unexpected error: %v" , err )
2015-10-01 21:56:22 +00:00
}
2015-10-16 21:36:49 +00:00
groupVersions = append ( groupVersions , ExtractGroupVersions ( & apiGroupList ) ... )
2015-10-01 21:56:22 +00:00
return groupVersions , nil
}
2015-06-13 02:06:18 +00:00
// NegotiateVersion queries the server's supported api versions to find
// a version that both client and server support.
2015-06-17 03:04:51 +00:00
// - If no version is provided, try registered client versions in order of
2015-06-13 02:06:18 +00:00
// preference.
// - If version is provided, but not default config (explicitly requested via
// commandline flag), and is unsupported by the server, print a warning to
// stderr and try client's registered versions in order of preference.
// - If version is config default, and the server does not support it,
// return an error.
2015-06-30 02:30:14 +00:00
func NegotiateVersion ( client * Client , c * Config , version string , clientRegisteredVersions [ ] string ) ( string , error ) {
2015-06-17 03:04:51 +00:00
var err error
if client == nil {
client , err = New ( c )
if err != nil {
return "" , err
}
2015-06-13 02:06:18 +00:00
}
2015-09-09 17:45:01 +00:00
clientVersions := sets . String { }
2015-06-30 02:30:14 +00:00
for _ , v := range clientRegisteredVersions {
2015-06-13 02:06:18 +00:00
clientVersions . Insert ( v )
}
apiVersions , err := client . ServerAPIVersions ( )
if err != nil {
2015-08-09 15:41:52 +00:00
return "" , fmt . Errorf ( "couldn't read version from server: %v" , err )
2015-06-13 02:06:18 +00:00
}
2015-09-09 17:45:01 +00:00
serverVersions := sets . String { }
2015-06-13 02:06:18 +00:00
for _ , v := range apiVersions . Versions {
serverVersions . Insert ( v )
}
// If no version requested, use config version (may also be empty).
if len ( version ) == 0 {
version = c . Version
}
// If version explicitly requested verify that both client and server support it.
// If server does not support warn, but try to negotiate a lower version.
if len ( version ) != 0 {
if ! clientVersions . Has ( version ) {
return "" , fmt . Errorf ( "Client does not support API version '%s'. Client supported API versions: %v" , version , clientVersions )
}
if serverVersions . Has ( version ) {
return version , nil
}
// If we are using an explicit config version the server does not support, fail.
if version == c . Version {
return "" , fmt . Errorf ( "Server does not support API version '%s'." , version )
}
}
2015-06-30 02:30:14 +00:00
for _ , clientVersion := range clientRegisteredVersions {
2015-06-13 02:06:18 +00:00
if serverVersions . Has ( clientVersion ) {
// Version was not explicitly requested in command config (--api-version).
// Ok to fall back to a supported version with a warning.
2015-10-01 04:18:50 +00:00
// TODO: caesarxuchao: enable the warning message when we have
// proper fix. Please refer to issue #14895.
// if len(version) != 0 {
// glog.Warningf("Server does not support API version '%s'. Falling back to '%s'.", version, clientVersion)
// }
2015-06-13 02:06:18 +00:00
return clientVersion , nil
}
}
return "" , fmt . Errorf ( "Failed to negotiate an api version. Server supports: %v. Client supports: %v." ,
2015-06-30 02:30:14 +00:00
serverVersions , clientRegisteredVersions )
2015-06-13 02:06:18 +00:00
}
2014-09-30 00:15:00 +00:00
// NewOrDie creates a Kubernetes client and panics if the provided API version is not recognized.
func NewOrDie ( c * Config ) * Client {
client , err := New ( c )
if err != nil {
panic ( err )
}
return client
}
2015-05-29 21:31:00 +00:00
// InClusterConfig returns a config object which uses the service account
// kubernetes gives to pods. It's intended for clients that expect to be
// running inside a pod running on kuberenetes. It will return an error if
// called from a process not running in a kubernetes environment.
func InClusterConfig ( ) ( * Config , error ) {
2015-06-24 03:54:19 +00:00
token , err := ioutil . ReadFile ( "/var/run/secrets/kubernetes.io/serviceaccount/" + api . ServiceAccountTokenKey )
2015-05-29 21:31:00 +00:00
if err != nil {
return nil , err
}
2015-06-24 03:54:19 +00:00
tlsClientConfig := TLSClientConfig { }
rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/" + api . ServiceAccountRootCAKey
if _ , err := util . CertPoolFromFile ( rootCAFile ) ; err != nil {
2015-09-29 07:04:07 +00:00
glog . Errorf ( "Expected to load root CA config from %s, but got err: %v" , rootCAFile , err )
2015-06-24 03:54:19 +00:00
} else {
tlsClientConfig . CAFile = rootCAFile
}
2015-05-29 21:31:00 +00:00
return & Config {
// TODO: switch to using cluster DNS.
2015-06-24 03:54:19 +00:00
Host : "https://" + net . JoinHostPort ( os . Getenv ( "KUBERNETES_SERVICE_HOST" ) , os . Getenv ( "KUBERNETES_SERVICE_PORT" ) ) ,
BearerToken : string ( token ) ,
TLSClientConfig : tlsClientConfig ,
2015-05-29 21:31:00 +00:00
} , nil
}
// NewInCluster is a shortcut for calling InClusterConfig() and then New().
func NewInCluster ( ) ( * Client , error ) {
cc , err := InClusterConfig ( )
if err != nil {
return nil , err
}
return New ( cc )
}
2015-01-06 17:36:08 +00:00
// SetKubernetesDefaults sets default values on the provided client config for accessing the
// Kubernetes API or returns an error if any of the defaults are impossible or invalid.
2015-09-24 01:17:09 +00:00
// TODO: this method needs to be split into one that sets defaults per group, expected to be fix in PR "Refactoring clientcache.go and helper.go #14592"
2015-01-06 17:36:08 +00:00
func SetKubernetesDefaults ( config * Config ) error {
if config . Prefix == "" {
config . Prefix = "/api"
}
2015-02-04 21:35:53 +00:00
if len ( config . UserAgent ) == 0 {
config . UserAgent = DefaultKubernetesUserAgent ( )
}
2015-01-06 17:36:08 +00:00
if len ( config . Version ) == 0 {
config . Version = defaultVersionFor ( config )
}
version := config . Version
2015-09-10 19:30:47 +00:00
versionInterfaces , err := latest . GroupOrDie ( "" ) . InterfacesFor ( version )
2014-09-30 00:15:00 +00:00
if err != nil {
2015-09-10 19:30:47 +00:00
return fmt . Errorf ( "API version '%s' is not recognized (valid values: %s)" , version , strings . Join ( latest . GroupOrDie ( "" ) . Versions , ", " ) )
2015-01-06 17:36:08 +00:00
}
if config . Codec == nil {
config . Codec = versionInterfaces . Codec
}
2015-03-31 03:21:21 +00:00
if config . QPS == 0.0 {
config . QPS = 5.0
}
2015-04-09 17:12:52 +00:00
if config . Burst == 0 {
config . Burst = 10
}
2015-01-06 17:36:08 +00:00
return nil
}
// RESTClientFor returns a RESTClient that satisfies the requested attributes on a client Config
// object. Note that a RESTClient may require fields that are optional when initializing a Client.
// A RESTClient created by this method is generic - it expects to operate on an API that follows
// the Kubernetes conventions, but may not be the Kubernetes API.
func RESTClientFor ( config * Config ) ( * RESTClient , error ) {
if len ( config . Version ) == 0 {
return nil , fmt . Errorf ( "version is required when initializing a RESTClient" )
}
if config . Codec == nil {
return nil , fmt . Errorf ( "Codec is required when initializing a RESTClient" )
2014-09-30 00:15:00 +00:00
}
baseURL , err := defaultServerUrlFor ( config )
if err != nil {
return nil , err
}
2015-06-15 22:15:55 +00:00
client := NewRESTClient ( baseURL , config . Version , config . Codec , config . QPS , config . Burst )
2014-09-30 00:15:00 +00:00
transport , err := TransportFor ( config )
if err != nil {
return nil , err
}
if transport != http . DefaultTransport {
client . Client = & http . Client { Transport : transport }
}
return client , nil
}
2015-07-24 17:55:11 +00:00
var (
// tlsTransports stores reusable round trippers with custom TLSClientConfig options
tlsTransports = map [ string ] * http . Transport { }
// tlsTransportLock protects retrieval and storage of round trippers into the tlsTransports map
tlsTransportLock sync . Mutex
)
// tlsTransportFor returns a http.RoundTripper for the given config, or an error
// The same RoundTripper will be returned for configs with identical TLS options
// If the config has no custom TLS options, http.DefaultTransport is returned
func tlsTransportFor ( config * Config ) ( http . RoundTripper , error ) {
// Get a unique key for the TLS options in the config
key , err := tlsConfigKey ( config )
if err != nil {
return nil , err
}
// Ensure we only create a single transport for the given TLS options
tlsTransportLock . Lock ( )
defer tlsTransportLock . Unlock ( )
// See if we already have a custom transport for this config
if cachedTransport , ok := tlsTransports [ key ] ; ok {
return cachedTransport , nil
}
// Get the TLS options for this client config
tlsConfig , err := TLSConfigFor ( config )
if err != nil {
return nil , err
}
// The options didn't require a custom TLS config
if tlsConfig == nil {
return http . DefaultTransport , nil
}
// Cache a single transport for these options
2015-10-02 04:30:49 +00:00
tlsTransports [ key ] = util . SetTransportDefaults ( & http . Transport {
2015-07-24 17:55:11 +00:00
TLSClientConfig : tlsConfig ,
2015-10-02 04:30:49 +00:00
} )
2015-07-24 17:55:11 +00:00
return tlsTransports [ key ] , nil
}
2014-09-30 00:15:00 +00:00
// TransportFor returns an http.RoundTripper that will provide the authentication
// or transport level security defined by the provided Config. Will return the
// default http.DefaultTransport if no special case behavior is needed.
func TransportFor ( config * Config ) ( http . RoundTripper , error ) {
2015-01-16 16:51:53 +00:00
hasCA := len ( config . CAFile ) > 0 || len ( config . CAData ) > 0
hasCert := len ( config . CertFile ) > 0 || len ( config . CertData ) > 0
2014-09-30 00:15:00 +00:00
// Set transport level security
2015-01-16 16:51:53 +00:00
if config . Transport != nil && ( hasCA || hasCert || config . Insecure ) {
2014-09-30 00:15:00 +00:00
return nil , fmt . Errorf ( "using a custom transport with TLS certificate options or the insecure flag is not allowed" )
}
2015-01-29 22:43:09 +00:00
2015-07-24 17:55:11 +00:00
var (
transport http . RoundTripper
err error
)
2015-01-29 22:43:09 +00:00
if config . Transport != nil {
2014-09-30 00:15:00 +00:00
transport = config . Transport
2015-01-29 22:43:09 +00:00
} else {
2015-07-24 17:55:11 +00:00
transport , err = tlsTransportFor ( config )
if err != nil {
return nil , err
2015-01-16 16:51:53 +00:00
}
2014-09-30 00:15:00 +00:00
}
2015-06-18 17:00:29 +00:00
2015-07-24 17:55:11 +00:00
// Call wrap prior to adding debugging wrappers
if config . WrapTransport != nil {
transport = config . WrapTransport ( transport )
}
2015-06-18 17:00:29 +00:00
switch {
case bool ( glog . V ( 9 ) ) :
transport = NewDebuggingRoundTripper ( transport , CurlCommand , URLTiming , ResponseHeaders )
case bool ( glog . V ( 8 ) ) :
transport = NewDebuggingRoundTripper ( transport , JustURL , RequestHeaders , ResponseStatus , ResponseHeaders )
case bool ( glog . V ( 7 ) ) :
transport = NewDebuggingRoundTripper ( transport , JustURL , RequestHeaders , ResponseStatus )
case bool ( glog . V ( 6 ) ) :
transport = NewDebuggingRoundTripper ( transport , URLTiming )
}
2015-01-29 22:43:09 +00:00
transport , err = HTTPWrappersForConfig ( config , transport )
if err != nil {
return nil , err
}
// TODO: use the config context to wrap a transport
return transport , nil
}
// HTTPWrappersForConfig wraps a round tripper with any relevant layered behavior from the
// config. Exposed to allow more clients that need HTTP-like behavior but then must hijack
// the underlying connection (like WebSocket or HTTP2 clients). Pure HTTP clients should use
// the higher level TransportFor or RESTClientFor methods.
func HTTPWrappersForConfig ( config * Config , rt http . RoundTripper ) ( http . RoundTripper , error ) {
2014-09-30 00:15:00 +00:00
// Set authentication wrappers
hasBasicAuth := config . Username != "" || config . Password != ""
if hasBasicAuth && config . BearerToken != "" {
return nil , fmt . Errorf ( "username/password or bearer token may be set, but not both" )
}
switch {
case config . BearerToken != "" :
2015-01-29 22:43:09 +00:00
rt = NewBearerAuthRoundTripper ( config . BearerToken , rt )
2014-09-30 00:15:00 +00:00
case hasBasicAuth :
2015-01-29 22:43:09 +00:00
rt = NewBasicAuthRoundTripper ( config . Username , config . Password , rt )
2014-09-30 00:15:00 +00:00
}
2015-02-04 21:35:53 +00:00
if len ( config . UserAgent ) > 0 {
rt = NewUserAgentRoundTripper ( config . UserAgent , rt )
}
2015-01-29 22:43:09 +00:00
return rt , nil
2014-09-30 00:15:00 +00:00
}
// DefaultServerURL converts a host, host:port, or URL string to the default base server API path
// to use with a Client at a given API version following the standard conventions for a
// Kubernetes API.
2014-11-14 05:42:36 +00:00
func DefaultServerURL ( host , prefix , version string , defaultTLS bool ) ( * url . URL , error ) {
2014-09-30 00:15:00 +00:00
if host == "" {
return nil , fmt . Errorf ( "host must be a URL or a host:port pair" )
}
base := host
hostURL , err := url . Parse ( base )
if err != nil {
return nil , err
}
if hostURL . Scheme == "" {
scheme := "http://"
2014-11-14 05:42:36 +00:00
if defaultTLS {
2014-09-30 00:15:00 +00:00
scheme = "https://"
}
hostURL , err = url . Parse ( scheme + base )
if err != nil {
return nil , err
}
if hostURL . Path != "" && hostURL . Path != "/" {
2015-08-27 21:30:31 +00:00
return nil , fmt . Errorf ( "host must be a URL or a host:port pair: %q" , base )
2014-09-30 00:15:00 +00:00
}
}
// If the user specified a URL without a path component (http://server.com), automatically
2014-10-03 02:03:52 +00:00
// append the default prefix
2014-09-30 00:15:00 +00:00
if hostURL . Path == "" {
2014-10-03 02:03:52 +00:00
if prefix == "" {
prefix = "/"
}
hostURL . Path = prefix
2014-09-30 00:15:00 +00:00
}
// Add the version to the end of the path
hostURL . Path = path . Join ( hostURL . Path , version )
return hostURL , nil
}
2015-09-15 02:25:13 +00:00
// IsConfigTransportTLS returns true if and only if the provided config will result in a protected
2014-09-30 00:15:00 +00:00
// connection to the server when it is passed to client.New() or client.RESTClientFor().
// Use to determine when to send credentials over the wire.
//
// Note: the Insecure flag is ignored when testing for this value, so MITM attacks are
// still possible.
2015-01-08 15:22:09 +00:00
func IsConfigTransportTLS ( config Config ) bool {
// determination of TLS transport does not logically require a version to be specified
// modify the copy of the config we got to satisfy preconditions for defaultServerUrlFor
config . Version = defaultVersionFor ( & config )
baseURL , err := defaultServerUrlFor ( & config )
2014-09-30 00:15:00 +00:00
if err != nil {
return false
}
return baseURL . Scheme == "https"
}
2015-01-06 17:36:08 +00:00
// defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It
// requires Host and Version to be set prior to being called.
2014-09-30 00:15:00 +00:00
func defaultServerUrlFor ( config * Config ) ( * url . URL , error ) {
// TODO: move the default to secure when the apiserver supports TLS by default
2014-11-14 05:42:36 +00:00
// config.Insecure is taken to mean "I want HTTPS but don't bother checking the certs against a CA."
2015-02-18 02:37:43 +00:00
hasCA := len ( config . CAFile ) != 0 || len ( config . CAData ) != 0
hasCert := len ( config . CertFile ) != 0 || len ( config . CertData ) != 0
defaultTLS := hasCA || hasCert || config . Insecure
2014-09-30 00:15:00 +00:00
host := config . Host
if host == "" {
host = "localhost"
}
2015-01-06 17:36:08 +00:00
return DefaultServerURL ( host , config . Prefix , config . Version , defaultTLS )
2014-09-30 00:15:00 +00:00
}
// defaultVersionFor is shared between defaultServerUrlFor and RESTClientFor
func defaultVersionFor ( config * Config ) string {
version := config . Version
if version == "" {
// Clients default to the preferred code API version
// TODO: implement version negotiation (highest version supported by server)
2015-09-10 19:30:47 +00:00
version = latest . GroupOrDie ( "" ) . Version
2014-09-30 00:15:00 +00:00
}
return version
}
2015-02-04 21:35:53 +00:00
// DefaultKubernetesUserAgent returns the default user agent that clients can use.
func DefaultKubernetesUserAgent ( ) string {
commit := version . Get ( ) . GitCommit
if len ( commit ) > 7 {
commit = commit [ : 7 ]
}
if len ( commit ) == 0 {
commit = "unknown"
}
version := version . Get ( ) . GitVersion
seg := strings . SplitN ( version , "-" , 2 )
version = seg [ 0 ]
return fmt . Sprintf ( "%s/%s (%s/%s) kubernetes/%s" , path . Base ( os . Args [ 0 ] ) , version , gruntime . GOOS , gruntime . GOARCH , commit )
}