Merge pull request #17535 from mesosphere/jdef_abspath_breaks_proxies

Auto commit by PR queue bot
This commit is contained in:
k8s-merge-robot
2016-01-20 21:46:47 -08:00
17 changed files with 158 additions and 119 deletions

View File

@@ -40,14 +40,21 @@ import (
"k8s.io/kubernetes/pkg/version"
)
const (
legacyAPIPath = "/api"
defaultAPIPath = "/apis"
)
// 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.
// 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.
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
// APIPath is a sub-path that points to an API root.
APIPath 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.
@@ -180,7 +187,7 @@ func ExtractGroupVersions(l *unversioned.APIGroupList) []string {
// 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
// on the Version and Codec of the config, because it uses AbsPath and
// takes the raw response.
func ServerAPIVersions(c *Config) (groupVersions []string, err error) {
transport, err := TransportFor(c)
@@ -191,13 +198,14 @@ func ServerAPIVersions(c *Config) (groupVersions []string, err error) {
configCopy := *c
configCopy.GroupVersion = nil
configCopy.Prefix = ""
baseURL, err := defaultServerUrlFor(c)
configCopy.APIPath = ""
baseURL, _, err := defaultServerUrlFor(&configCopy)
if err != nil {
return nil, err
}
// Get the groupVersions exposed at /api
baseURL.Path = "/api"
originalPath := baseURL.Path
baseURL.Path = path.Join(originalPath, legacyAPIPath)
resp, err := client.Get(baseURL.String())
if err != nil {
return nil, err
@@ -211,7 +219,7 @@ func ServerAPIVersions(c *Config) (groupVersions []string, err error) {
groupVersions = append(groupVersions, v.Versions...)
// Get the groupVersions exposed at /apis
baseURL.Path = "/apis"
baseURL.Path = path.Join(originalPath, defaultAPIPath)
resp2, err := client.Get(baseURL.String())
if err != nil {
return nil, err
@@ -357,8 +365,8 @@ func NewInCluster() (*Client, error) {
// Kubernetes API or returns an error if any of the defaults are impossible or invalid.
// 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"
func SetKubernetesDefaults(config *Config) error {
if config.Prefix == "" {
config.Prefix = "/api"
if config.APIPath == "" {
config.APIPath = legacyAPIPath
}
if len(config.UserAgent) == 0 {
config.UserAgent = DefaultKubernetesUserAgent()
@@ -398,12 +406,12 @@ func RESTClientFor(config *Config) (*RESTClient, error) {
return nil, fmt.Errorf("Codec is required when initializing a RESTClient")
}
baseURL, err := defaultServerUrlFor(config)
baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
if err != nil {
return nil, err
}
client := NewRESTClient(baseURL, *config.GroupVersion, config.Codec, config.QPS, config.Burst)
client := NewRESTClient(baseURL, versionedAPIPath, *config.GroupVersion, config.Codec, config.QPS, config.Burst)
transport, err := TransportFor(config)
if err != nil {
@@ -423,12 +431,12 @@ func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
return nil, fmt.Errorf("Codec is required when initializing a RESTClient")
}
baseURL, err := defaultServerUrlFor(config)
baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
if err != nil {
return nil, err
}
client := NewRESTClient(baseURL, unversioned.SchemeGroupVersion, config.Codec, config.QPS, config.Burst)
client := NewRESTClient(baseURL, versionedAPIPath, unversioned.SchemeGroupVersion, config.Codec, config.QPS, config.Burst)
transport, err := TransportFor(config)
if err != nil {
@@ -444,14 +452,14 @@ func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
// 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.
func DefaultServerURL(host, prefix string, groupVersion unversioned.GroupVersion, defaultTLS bool) (*url.URL, error) {
func DefaultServerURL(host, apiPath string, groupVersion unversioned.GroupVersion, defaultTLS bool) (*url.URL, string, error) {
if host == "" {
return nil, fmt.Errorf("host must be a URL or a host:port pair")
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
return nil, "", err
}
if hostURL.Scheme == "" {
scheme := "http://"
@@ -460,32 +468,34 @@ func DefaultServerURL(host, prefix string, groupVersion unversioned.GroupVersion
}
hostURL, err = url.Parse(scheme + base)
if err != nil {
return nil, err
return nil, "", err
}
if hostURL.Path != "" && hostURL.Path != "/" {
return nil, fmt.Errorf("host must be a URL or a host:port pair: %q", base)
return nil, "", fmt.Errorf("host must be a URL or a host:port pair: %q", base)
}
}
// If the user specified a URL without a path component (http://server.com), automatically
// append the default prefix
if hostURL.Path == "" {
if prefix == "" {
prefix = "/"
}
hostURL.Path = prefix
}
// 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)
// Add the version to the end of the path
if len(groupVersion.Group) > 0 {
hostURL.Path = path.Join(hostURL.Path, groupVersion.Group, groupVersion.Version)
versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Group, groupVersion.Version)
} else {
hostURL.Path = path.Join(hostURL.Path, groupVersion.Version)
versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Version)
}
return hostURL, nil
return hostURL, versionedAPIPath, nil
}
// IsConfigTransportTLS returns true if and only if the provided config will result in a protected
@@ -499,7 +509,7 @@ func IsConfigTransportTLS(config Config) bool {
// modify the copy of the config we got to satisfy preconditions for defaultServerUrlFor
config.GroupVersion = defaultVersionFor(&config)
baseURL, err := defaultServerUrlFor(&config)
baseURL, _, err := defaultServerUrlFor(&config)
if err != nil {
return false
}
@@ -508,7 +518,7 @@ func IsConfigTransportTLS(config Config) bool {
// defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It
// requires Host and Version to be set prior to being called.
func defaultServerUrlFor(config *Config) (*url.URL, error) {
func defaultServerUrlFor(config *Config) (*url.URL, string, error) {
// TODO: move the default to secure when the apiserver supports TLS by default
// config.Insecure is taken to mean "I want HTTPS but don't bother checking the certs against a CA."
hasCA := len(config.CAFile) != 0 || len(config.CAData) != 0
@@ -520,12 +530,12 @@ func defaultServerUrlFor(config *Config) (*url.URL, error) {
}
if config.GroupVersion != nil {
return DefaultServerURL(host, config.Prefix, *config.GroupVersion, defaultTLS)
return DefaultServerURL(host, config.APIPath, *config.GroupVersion, defaultTLS)
}
return DefaultServerURL(host, config.Prefix, unversioned.GroupVersion{}, defaultTLS)
return DefaultServerURL(host, config.APIPath, unversioned.GroupVersion{}, defaultTLS)
}
// defaultVersionFor is shared between defaultServerUrlFor and RESTClientFor
// defaultVersionFor is shared between IsConfigTransportTLS and RESTClientFor
func defaultVersionFor(config *Config) *unversioned.GroupVersion {
if config.GroupVersion == nil {
// Clients default to the preferred code API version