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-08-05 22:05:17 +00:00
"github.com/golang/glog"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
2015-10-12 20:48:14 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2016-01-13 22:40:56 +00:00
"k8s.io/kubernetes/pkg/apimachinery/registered"
2015-12-08 14:21:04 +00:00
"k8s.io/kubernetes/pkg/apis/extensions"
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
)
2016-01-06 23:59:54 +00:00
const (
legacyAPIPath = "/api"
defaultAPIPath = "/apis"
)
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 {
2016-01-06 23:59:54 +00:00
// Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
// If a URL is given then the (optional) Path of that URL represents a prefix that must
// be appended to all request URIs used to access the apiserver. This allows a frontend
// proxy to easily relocate all of the apiserver endpoints.
2014-10-03 02:03:52 +00:00
Host string
2016-01-06 23:59:54 +00:00
// APIPath is a sub-path that points to an API root.
APIPath string
2015-12-25 23:05:01 +00:00
// 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
// ContentConfig contains settings that affect how objects are transformed when
// sent to the server.
ContentConfig
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
}
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
}
2015-12-25 23:05:01 +00:00
type ContentConfig struct {
// ContentType specifies the wire format used to communicate with the server.
// This value will be set as the Accept header on requests made to the server, and
// as the default content type on any object sent to the server. If not set,
// "application/json" is used.
ContentType string
// GroupVersion 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.
GroupVersion * unversioned . GroupVersion
// 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
// 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
}
2016-01-13 22:40:56 +00:00
if _ , err := registered . Group ( extensions . GroupName ) ; 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 ( )
2016-01-05 19:55:34 +00:00
serverVersion , err := client . Discovery ( ) . ServerVersion ( )
2015-01-07 20:59:22 +00:00
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
2016-01-06 23:59:54 +00:00
// on the Version and Codec of the config, because it uses AbsPath and
2015-10-01 21:56:22 +00:00
// 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
2015-11-13 21:20:54 +00:00
configCopy . GroupVersion = nil
2016-01-06 23:59:54 +00:00
configCopy . APIPath = ""
baseURL , _ , err := defaultServerUrlFor ( & configCopy )
2015-10-01 21:56:22 +00:00
if err != nil {
return nil , err
}
// Get the groupVersions exposed at /api
2016-01-06 23:59:54 +00:00
originalPath := baseURL . Path
baseURL . Path = path . Join ( originalPath , legacyAPIPath )
2015-10-07 23:19:22 +00:00
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
2016-01-06 23:59:54 +00:00
baseURL . Path = path . Join ( originalPath , defaultAPIPath )
2015-10-07 23:19:22 +00:00
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-11-13 21:20:54 +00:00
func NegotiateVersion ( client * Client , c * Config , requestedGV * unversioned . GroupVersion , clientRegisteredGVs [ ] unversioned . GroupVersion ) ( * unversioned . GroupVersion , error ) {
2015-06-17 03:04:51 +00:00
var err error
if client == nil {
client , err = New ( c )
if err != nil {
2015-11-13 21:20:54 +00:00
return nil , err
2015-06-17 03:04:51 +00:00
}
2015-06-13 02:06:18 +00:00
}
2015-09-09 17:45:01 +00:00
clientVersions := sets . String { }
2015-11-13 21:20:54 +00:00
for _ , gv := range clientRegisteredGVs {
clientVersions . Insert ( gv . String ( ) )
2015-06-13 02:06:18 +00:00
}
2015-12-11 01:51:32 +00:00
groups , err := client . ServerGroups ( )
2015-06-13 02:06:18 +00:00
if err != nil {
2015-10-20 15:45:46 +00:00
// This is almost always a connection error, and higher level code should treat this as a generic error,
// not a negotiation specific error.
2015-11-13 21:20:54 +00:00
return nil , err
2015-06-13 02:06:18 +00:00
}
2015-12-11 01:51:32 +00:00
versions := ExtractGroupVersions ( groups )
2015-09-09 17:45:01 +00:00
serverVersions := sets . String { }
2015-12-11 01:51:32 +00:00
for _ , v := range versions {
2015-06-13 02:06:18 +00:00
serverVersions . Insert ( v )
}
2015-11-13 21:20:54 +00:00
2015-06-13 02:06:18 +00:00
// If no version requested, use config version (may also be empty).
2015-11-13 21:20:54 +00:00
// make a copy of the original so we don't risk mutating input here or in the returned value
var preferredGV * unversioned . GroupVersion
switch {
case requestedGV != nil :
t := * requestedGV
preferredGV = & t
case c . GroupVersion != nil :
t := * c . GroupVersion
preferredGV = & t
2015-06-13 02:06:18 +00:00
}
2015-11-13 21:20:54 +00:00
2015-06-13 02:06:18 +00:00
// 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.
2015-11-13 21:20:54 +00:00
if preferredGV != nil {
if ! clientVersions . Has ( preferredGV . String ( ) ) {
return nil , fmt . Errorf ( "client does not support API version %q; client supported API versions: %v" , preferredGV , clientVersions )
2015-06-13 02:06:18 +00:00
}
2015-11-13 21:20:54 +00:00
if serverVersions . Has ( preferredGV . String ( ) ) {
return preferredGV , nil
2015-06-13 02:06:18 +00:00
}
// If we are using an explicit config version the server does not support, fail.
2015-11-13 21:20:54 +00:00
if ( c . GroupVersion != nil ) && ( * preferredGV == * c . GroupVersion ) {
return nil , fmt . Errorf ( "server does not support API version %q" , preferredGV )
2015-06-13 02:06:18 +00:00
}
}
2015-11-13 21:20:54 +00:00
for _ , clientGV := range clientRegisteredGVs {
if serverVersions . Has ( clientGV . String ( ) ) {
2015-06-13 02:06:18 +00:00
// 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-11-13 21:20:54 +00:00
t := clientGV
return & t , nil
2015-06-13 02:06:18 +00:00
}
}
2015-11-13 21:20:54 +00:00
return nil , fmt . Errorf ( "failed to negotiate an api version; server supports: %v, client supports: %v" ,
serverVersions , clientVersions )
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-11-05 19:59:22 +00:00
host , port := os . Getenv ( "KUBERNETES_SERVICE_HOST" ) , os . Getenv ( "KUBERNETES_SERVICE_PORT" )
if len ( host ) == 0 || len ( port ) == 0 {
return nil , fmt . Errorf ( "unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined" )
}
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-11-05 19:59:22 +00:00
Host : "https://" + net . JoinHostPort ( host , port ) ,
2015-06-24 03:54:19 +00:00
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 {
2016-01-06 23:59:54 +00:00
if config . APIPath == "" {
config . APIPath = legacyAPIPath
2015-01-06 17:36:08 +00:00
}
2015-02-04 21:35:53 +00:00
if len ( config . UserAgent ) == 0 {
config . UserAgent = DefaultKubernetesUserAgent ( )
}
2016-01-13 22:40:56 +00:00
g , err := registered . Group ( api . GroupName )
2015-12-11 01:51:32 +00:00
if err != nil {
return err
2015-01-06 17:36:08 +00:00
}
2015-12-11 01:51:32 +00:00
// TODO: Unconditionally set the config.Version, until we fix the config.
copyGroupVersion := g . GroupVersion
config . GroupVersion = & copyGroupVersion
2015-01-06 17:36:08 +00:00
if config . Codec == nil {
2015-12-21 05:32:52 +00:00
config . Codec = api . Codecs . LegacyCodec ( * config . GroupVersion )
2015-01-06 17:36:08 +00:00
}
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 ) {
2015-11-13 21:20:54 +00:00
if config . GroupVersion == nil {
return nil , fmt . Errorf ( "GroupVersion is required when initializing a RESTClient" )
2015-01-06 17:36:08 +00:00
}
if config . Codec == nil {
return nil , fmt . Errorf ( "Codec is required when initializing a RESTClient" )
2014-09-30 00:15:00 +00:00
}
2016-01-06 23:59:54 +00:00
baseURL , versionedAPIPath , err := defaultServerUrlFor ( config )
2014-09-30 00:15:00 +00:00
if err != nil {
return nil , err
}
transport , err := TransportFor ( config )
if err != nil {
return nil , err
}
2015-12-25 23:05:01 +00:00
var httpClient * http . Client
2014-09-30 00:15:00 +00:00
if transport != http . DefaultTransport {
2015-12-25 23:05:01 +00:00
httpClient = & http . Client { Transport : transport }
2014-09-30 00:15:00 +00:00
}
2015-12-25 23:05:01 +00:00
client := NewRESTClient ( baseURL , versionedAPIPath , config . ContentConfig , config . QPS , config . Burst , httpClient )
2014-09-30 00:15:00 +00:00
return client , nil
}
2015-10-20 21:33:43 +00:00
// UnversionedRESTClientFor is the same as RESTClientFor, except that it allows
// the config.Version to be empty.
func UnversionedRESTClientFor ( config * Config ) ( * RESTClient , error ) {
if config . Codec == nil {
return nil , fmt . Errorf ( "Codec is required when initializing a RESTClient" )
}
2016-01-06 23:59:54 +00:00
baseURL , versionedAPIPath , err := defaultServerUrlFor ( config )
2015-10-20 21:33:43 +00:00
if err != nil {
return nil , err
}
transport , err := TransportFor ( config )
if err != nil {
return nil , err
}
2015-12-25 23:05:01 +00:00
var httpClient * http . Client
2015-10-20 21:33:43 +00:00
if transport != http . DefaultTransport {
2015-12-25 23:05:01 +00:00
httpClient = & http . Client { Transport : transport }
2015-10-20 21:33:43 +00:00
}
2015-12-25 23:05:01 +00:00
versionConfig := config . ContentConfig
if versionConfig . GroupVersion == nil {
v := unversioned . SchemeGroupVersion
versionConfig . GroupVersion = & v
}
client := NewRESTClient ( baseURL , versionedAPIPath , versionConfig , config . QPS , config . Burst , httpClient )
2015-10-20 21:33:43 +00:00
return client , 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.
2016-01-06 23:59:54 +00:00
func DefaultServerURL ( host , apiPath string , groupVersion unversioned . GroupVersion , defaultTLS bool ) ( * url . URL , string , error ) {
2014-09-30 00:15:00 +00:00
if host == "" {
2016-01-06 23:59:54 +00:00
return nil , "" , fmt . Errorf ( "host must be a URL or a host:port pair" )
2014-09-30 00:15:00 +00:00
}
base := host
hostURL , err := url . Parse ( base )
if err != nil {
2016-01-06 23:59:54 +00:00
return nil , "" , err
2014-09-30 00:15:00 +00:00
}
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 {
2016-01-06 23:59:54 +00:00
return nil , "" , err
2014-09-30 00:15:00 +00:00
}
if hostURL . Path != "" && hostURL . Path != "/" {
2016-01-06 23:59:54 +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
}
}
2016-01-06 23:59:54 +00:00
// hostURL.Path is optional; a non-empty Path is treated as a prefix that is to be applied to
// all URIs used to access the host. this is useful when there's a proxy in front of the
// apiserver that has relocated the apiserver endpoints, forwarding all requests from, for
// example, /a/b/c to the apiserver. in this case the Path should be /a/b/c.
//
// if running without a frontend proxy (that changes the location of the apiserver), then
// hostURL.Path should be blank.
//
// versionedAPIPath, a path relative to baseURL.Path, points to a versioned API base
versionedAPIPath := path . Join ( "/" , apiPath )
2014-09-30 00:15:00 +00:00
// Add the version to the end of the path
2015-11-13 21:20:54 +00:00
if len ( groupVersion . Group ) > 0 {
2016-01-06 23:59:54 +00:00
versionedAPIPath = path . Join ( versionedAPIPath , groupVersion . Group , groupVersion . Version )
2015-11-13 21:20:54 +00:00
} else {
2016-01-06 23:59:54 +00:00
versionedAPIPath = path . Join ( versionedAPIPath , groupVersion . Version )
2015-11-13 21:20:54 +00:00
}
2014-09-30 00:15:00 +00:00
2016-01-06 23:59:54 +00:00
return hostURL , versionedAPIPath , nil
2014-09-30 00:15:00 +00:00
}
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
2015-11-13 21:20:54 +00:00
config . GroupVersion = defaultVersionFor ( & config )
2015-01-08 15:22:09 +00:00
2016-01-06 23:59:54 +00:00
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.
2016-01-06 23:59:54 +00:00
func defaultServerUrlFor ( config * Config ) ( * url . URL , string , error ) {
2014-09-30 00:15:00 +00:00
// 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-11-13 21:20:54 +00:00
if config . GroupVersion != nil {
2016-01-06 23:59:54 +00:00
return DefaultServerURL ( host , config . APIPath , * config . GroupVersion , defaultTLS )
2015-11-13 21:20:54 +00:00
}
2016-01-06 23:59:54 +00:00
return DefaultServerURL ( host , config . APIPath , unversioned . GroupVersion { } , defaultTLS )
2014-09-30 00:15:00 +00:00
}
2016-01-06 23:59:54 +00:00
// defaultVersionFor is shared between IsConfigTransportTLS and RESTClientFor
2015-11-13 21:20:54 +00:00
func defaultVersionFor ( config * Config ) * unversioned . GroupVersion {
if config . GroupVersion == nil {
2014-09-30 00:15:00 +00:00
// Clients default to the preferred code API version
// TODO: implement version negotiation (highest version supported by server)
2015-11-13 21:20:54 +00:00
// TODO this drops out when groupmeta is refactored
2016-01-13 22:40:56 +00:00
copyGroupVersion := registered . GroupOrDie ( api . GroupName ) . GroupVersion
2015-11-16 18:34:28 +00:00
return & copyGroupVersion
2014-09-30 00:15:00 +00:00
}
2015-11-13 21:20:54 +00:00
return config . GroupVersion
2014-09-30 00:15:00 +00:00
}
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 )
}