mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	AbsPath should be compatible with proxy-prefixes:
- replace Config.Prefix with .Host and .APIPath - Request .path promoted to .pathPrefix, .baseURL holds required prefix
This commit is contained in:
		@@ -53,7 +53,7 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
 | 
				
			|||||||
	sw := generator.NewSnippetWriter(w, c, "$", "$")
 | 
						sw := generator.NewSnippetWriter(w, c, "$", "$")
 | 
				
			||||||
	const pkgUnversioned = "k8s.io/kubernetes/pkg/client/unversioned"
 | 
						const pkgUnversioned = "k8s.io/kubernetes/pkg/client/unversioned"
 | 
				
			||||||
	const pkgLatest = "k8s.io/kubernetes/pkg/api/latest"
 | 
						const pkgLatest = "k8s.io/kubernetes/pkg/api/latest"
 | 
				
			||||||
	prefix := func(group string) string {
 | 
						apiPath := func(group string) string {
 | 
				
			||||||
		if group == "legacy" {
 | 
							if group == "legacy" {
 | 
				
			||||||
			return `"/api"`
 | 
								return `"/api"`
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -78,7 +78,7 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
 | 
				
			|||||||
		"RESTClientFor":              c.Universe.Function(types.Name{Package: pkgUnversioned, Name: "RESTClientFor"}),
 | 
							"RESTClientFor":              c.Universe.Function(types.Name{Package: pkgUnversioned, Name: "RESTClientFor"}),
 | 
				
			||||||
		"latestGroup":                c.Universe.Variable(types.Name{Package: pkgLatest, Name: "Group"}),
 | 
							"latestGroup":                c.Universe.Variable(types.Name{Package: pkgLatest, Name: "Group"}),
 | 
				
			||||||
		"GroupOrDie":                 c.Universe.Variable(types.Name{Package: pkgLatest, Name: "GroupOrDie"}),
 | 
							"GroupOrDie":                 c.Universe.Variable(types.Name{Package: pkgLatest, Name: "GroupOrDie"}),
 | 
				
			||||||
		"prefix":                     prefix(g.group),
 | 
							"apiPath":                    apiPath(g.group),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sw.Do(groupInterfaceTemplate, m)
 | 
						sw.Do(groupInterfaceTemplate, m)
 | 
				
			||||||
	sw.Do(groupClientTemplate, m)
 | 
						sw.Do(groupClientTemplate, m)
 | 
				
			||||||
@@ -157,7 +157,7 @@ func setConfigDefaults(config *$.Config|raw$) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	config.Prefix = $.prefix$
 | 
						config.APIPath = $.apiPath$
 | 
				
			||||||
	if config.UserAgent == "" {
 | 
						if config.UserAgent == "" {
 | 
				
			||||||
		config.UserAgent = $.DefaultKubernetesUserAgent|raw$()
 | 
							config.UserAgent = $.DefaultKubernetesUserAgent|raw$()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,7 +69,7 @@ func setConfigDefaults(config *unversioned.Config) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	config.Prefix = "/apis"
 | 
						config.APIPath = "/apis"
 | 
				
			||||||
	if config.UserAgent == "" {
 | 
						if config.UserAgent == "" {
 | 
				
			||||||
		config.UserAgent = unversioned.DefaultKubernetesUserAgent()
 | 
							config.UserAgent = unversioned.DefaultKubernetesUserAgent()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,7 +94,7 @@ func setConfigDefaults(config *unversioned.Config) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	config.Prefix = "/apis"
 | 
						config.APIPath = "/apis"
 | 
				
			||||||
	if config.UserAgent == "" {
 | 
						if config.UserAgent == "" {
 | 
				
			||||||
		config.UserAgent = unversioned.DefaultKubernetesUserAgent()
 | 
							config.UserAgent = unversioned.DefaultKubernetesUserAgent()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -139,7 +139,7 @@ func setConfigDefaults(config *unversioned.Config) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	config.Prefix = "/api"
 | 
						config.APIPath = "/api"
 | 
				
			||||||
	if config.UserAgent == "" {
 | 
						if config.UserAgent == "" {
 | 
				
			||||||
		config.UserAgent = unversioned.DefaultKubernetesUserAgent()
 | 
							config.UserAgent = unversioned.DefaultKubernetesUserAgent()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,8 +94,6 @@ func (config DirectClientConfig) ClientConfig() (*client.Config, error) {
 | 
				
			|||||||
	clientConfig := &client.Config{}
 | 
						clientConfig := &client.Config{}
 | 
				
			||||||
	clientConfig.Host = configClusterInfo.Server
 | 
						clientConfig.Host = configClusterInfo.Server
 | 
				
			||||||
	if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 {
 | 
						if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 {
 | 
				
			||||||
		clientConfig.Prefix = u.Path
 | 
					 | 
				
			||||||
		u.Path = ""
 | 
					 | 
				
			||||||
		u.RawQuery = ""
 | 
							u.RawQuery = ""
 | 
				
			||||||
		u.Fragment = ""
 | 
							u.Fragment = ""
 | 
				
			||||||
		clientConfig.Host = u.String()
 | 
							clientConfig.Host = u.String()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -156,7 +156,7 @@ func TestCreateClean(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
 | 
						matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
 | 
				
			||||||
	matchStringArg("", clientConfig.Prefix, t)
 | 
						matchStringArg("", clientConfig.APIPath, t)
 | 
				
			||||||
	matchStringArg(config.Clusters["clean"].APIVersion, clientConfig.GroupVersion.String(), t)
 | 
						matchStringArg(config.Clusters["clean"].APIVersion, clientConfig.GroupVersion.String(), t)
 | 
				
			||||||
	matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
 | 
						matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
 | 
				
			||||||
	matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
 | 
						matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
 | 
				
			||||||
@@ -166,22 +166,21 @@ func TestCreateCleanWithPrefix(t *testing.T) {
 | 
				
			|||||||
	tt := []struct {
 | 
						tt := []struct {
 | 
				
			||||||
		server string
 | 
							server string
 | 
				
			||||||
		host   string
 | 
							host   string
 | 
				
			||||||
		prefix string
 | 
					 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{"https://anything.com:8080/foo/bar", "https://anything.com:8080", "/foo/bar"},
 | 
							{"https://anything.com:8080/foo/bar", "https://anything.com:8080/foo/bar"},
 | 
				
			||||||
		{"http://anything.com:8080/foo/bar", "http://anything.com:8080", "/foo/bar"},
 | 
							{"http://anything.com:8080/foo/bar", "http://anything.com:8080/foo/bar"},
 | 
				
			||||||
		{"http://anything.com:8080/foo/bar/", "http://anything.com:8080", "/foo/bar/"},
 | 
							{"http://anything.com:8080/foo/bar/", "http://anything.com:8080/foo/bar/"},
 | 
				
			||||||
		{"http://anything.com:8080/", "http://anything.com:8080/", ""},
 | 
							{"http://anything.com:8080/", "http://anything.com:8080/"},
 | 
				
			||||||
		{"http://anything.com:8080//", "http://anything.com:8080", "//"},
 | 
							{"http://anything.com:8080//", "http://anything.com:8080//"},
 | 
				
			||||||
		{"anything.com:8080/foo/bar", "anything.com:8080/foo/bar", ""},
 | 
							{"anything.com:8080/foo/bar", "anything.com:8080/foo/bar"},
 | 
				
			||||||
		{"anything.com:8080", "anything.com:8080", ""},
 | 
							{"anything.com:8080", "anything.com:8080"},
 | 
				
			||||||
		{"anything.com", "anything.com", ""},
 | 
							{"anything.com", "anything.com"},
 | 
				
			||||||
		{"anything", "anything", ""},
 | 
							{"anything", "anything"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// WARNING: EnvVarCluster.Server is set during package loading time and can not be overriden by os.Setenv inside this test
 | 
						// WARNING: EnvVarCluster.Server is set during package loading time and can not be overriden by os.Setenv inside this test
 | 
				
			||||||
	EnvVarCluster.Server = ""
 | 
						EnvVarCluster.Server = ""
 | 
				
			||||||
	tt = append(tt, struct{ server, host, prefix string }{"", "http://localhost:8080", ""})
 | 
						tt = append(tt, struct{ server, host string }{"", "http://localhost:8080"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, tc := range tt {
 | 
						for _, tc := range tt {
 | 
				
			||||||
		config := createValidTestConfig()
 | 
							config := createValidTestConfig()
 | 
				
			||||||
@@ -198,7 +197,6 @@ func TestCreateCleanWithPrefix(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		matchStringArg(tc.host, clientConfig.Host, t)
 | 
							matchStringArg(tc.host, clientConfig.Host, t)
 | 
				
			||||||
		matchStringArg(tc.prefix, clientConfig.Prefix, t)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -139,7 +139,7 @@ func (d *DiscoveryClient) ServerResources() (map[string]*unversioned.APIResource
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func setDiscoveryDefaults(config *Config) error {
 | 
					func setDiscoveryDefaults(config *Config) error {
 | 
				
			||||||
	config.Prefix = ""
 | 
						config.APIPath = ""
 | 
				
			||||||
	config.GroupVersion = nil
 | 
						config.GroupVersion = nil
 | 
				
			||||||
	// Discovery client deals with unversioned objects, so we use api.Codec.
 | 
						// Discovery client deals with unversioned objects, so we use api.Codec.
 | 
				
			||||||
	config.Codec = api.Codec
 | 
						config.Codec = api.Codec
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,7 +128,7 @@ func setExtensionsDefaults(config *Config) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	config.Prefix = "apis/"
 | 
						config.APIPath = defaultAPIPath
 | 
				
			||||||
	if config.UserAgent == "" {
 | 
						if config.UserAgent == "" {
 | 
				
			||||||
		config.UserAgent = DefaultKubernetesUserAgent()
 | 
							config.UserAgent = DefaultKubernetesUserAgent()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,23 +50,27 @@ type RESTClient struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *RESTClient) Get() *unversioned.Request {
 | 
					func (c *RESTClient) Get() *unversioned.Request {
 | 
				
			||||||
	return unversioned.NewRequest(c, "GET", &url.URL{Host: "localhost"}, *testapi.Default.GroupVersion(), c.Codec, nil)
 | 
						return c.request("GET")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *RESTClient) Put() *unversioned.Request {
 | 
					func (c *RESTClient) Put() *unversioned.Request {
 | 
				
			||||||
	return unversioned.NewRequest(c, "PUT", &url.URL{Host: "localhost"}, *testapi.Default.GroupVersion(), c.Codec, nil)
 | 
						return c.request("PUT")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *RESTClient) Patch(_ api.PatchType) *unversioned.Request {
 | 
					func (c *RESTClient) Patch(_ api.PatchType) *unversioned.Request {
 | 
				
			||||||
	return unversioned.NewRequest(c, "PATCH", &url.URL{Host: "localhost"}, *testapi.Default.GroupVersion(), c.Codec, nil)
 | 
						return c.request("PATCH")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *RESTClient) Post() *unversioned.Request {
 | 
					func (c *RESTClient) Post() *unversioned.Request {
 | 
				
			||||||
	return unversioned.NewRequest(c, "POST", &url.URL{Host: "localhost"}, *testapi.Default.GroupVersion(), c.Codec, nil)
 | 
						return c.request("POST")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *RESTClient) Delete() *unversioned.Request {
 | 
					func (c *RESTClient) Delete() *unversioned.Request {
 | 
				
			||||||
	return unversioned.NewRequest(c, "DELETE", &url.URL{Host: "localhost"}, *testapi.Default.GroupVersion(), c.Codec, nil)
 | 
						return c.request("DELETE")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *RESTClient) request(verb string) *unversioned.Request {
 | 
				
			||||||
 | 
						return unversioned.NewRequest(c, verb, &url.URL{Host: "localhost"}, "", *testapi.Default.GroupVersion(), c.Codec, nil)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *RESTClient) Do(req *http.Request) (*http.Response, error) {
 | 
					func (c *RESTClient) Do(req *http.Request) (*http.Response, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,14 +40,21 @@ import (
 | 
				
			|||||||
	"k8s.io/kubernetes/pkg/version"
 | 
						"k8s.io/kubernetes/pkg/version"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						legacyAPIPath  = "/api"
 | 
				
			||||||
 | 
						defaultAPIPath = "/apis"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Config holds the common attributes that can be passed to a Kubernetes client on
 | 
					// Config holds the common attributes that can be passed to a Kubernetes client on
 | 
				
			||||||
// initialization.
 | 
					// initialization.
 | 
				
			||||||
type Config struct {
 | 
					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
 | 
						Host string
 | 
				
			||||||
	// Prefix is the sub path of the server. If not specified, the client will set
 | 
						// APIPath is a sub-path that points to an API root.
 | 
				
			||||||
	// a default value.  Use "/" to indicate the server root should be used
 | 
						APIPath string
 | 
				
			||||||
	Prefix string
 | 
					 | 
				
			||||||
	// GroupVersion is the API version to talk to. Must be provided when initializing
 | 
						// 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
 | 
						// a RESTClient directly. When initializing a Client, will be set with the default
 | 
				
			||||||
	// code version.
 | 
						// code version.
 | 
				
			||||||
@@ -180,7 +187,7 @@ func ExtractGroupVersions(l *unversioned.APIGroupList) []string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ServerAPIVersions returns the GroupVersions supported by the API server.
 | 
					// ServerAPIVersions returns the GroupVersions supported by the API server.
 | 
				
			||||||
// It creates a RESTClient based on the passed in config, but it doesn't rely
 | 
					// 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.
 | 
					// takes the raw response.
 | 
				
			||||||
func ServerAPIVersions(c *Config) (groupVersions []string, err error) {
 | 
					func ServerAPIVersions(c *Config) (groupVersions []string, err error) {
 | 
				
			||||||
	transport, err := TransportFor(c)
 | 
						transport, err := TransportFor(c)
 | 
				
			||||||
@@ -191,13 +198,14 @@ func ServerAPIVersions(c *Config) (groupVersions []string, err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	configCopy := *c
 | 
						configCopy := *c
 | 
				
			||||||
	configCopy.GroupVersion = nil
 | 
						configCopy.GroupVersion = nil
 | 
				
			||||||
	configCopy.Prefix = ""
 | 
						configCopy.APIPath = ""
 | 
				
			||||||
	baseURL, err := defaultServerUrlFor(c)
 | 
						baseURL, _, err := defaultServerUrlFor(&configCopy)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Get the groupVersions exposed at /api
 | 
						// Get the groupVersions exposed at /api
 | 
				
			||||||
	baseURL.Path = "/api"
 | 
						originalPath := baseURL.Path
 | 
				
			||||||
 | 
						baseURL.Path = path.Join(originalPath, legacyAPIPath)
 | 
				
			||||||
	resp, err := client.Get(baseURL.String())
 | 
						resp, err := client.Get(baseURL.String())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -211,7 +219,7 @@ func ServerAPIVersions(c *Config) (groupVersions []string, err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	groupVersions = append(groupVersions, v.Versions...)
 | 
						groupVersions = append(groupVersions, v.Versions...)
 | 
				
			||||||
	// Get the groupVersions exposed at /apis
 | 
						// Get the groupVersions exposed at /apis
 | 
				
			||||||
	baseURL.Path = "/apis"
 | 
						baseURL.Path = path.Join(originalPath, defaultAPIPath)
 | 
				
			||||||
	resp2, err := client.Get(baseURL.String())
 | 
						resp2, err := client.Get(baseURL.String())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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.
 | 
					// 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"
 | 
					// 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 {
 | 
					func SetKubernetesDefaults(config *Config) error {
 | 
				
			||||||
	if config.Prefix == "" {
 | 
						if config.APIPath == "" {
 | 
				
			||||||
		config.Prefix = "/api"
 | 
							config.APIPath = legacyAPIPath
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(config.UserAgent) == 0 {
 | 
						if len(config.UserAgent) == 0 {
 | 
				
			||||||
		config.UserAgent = DefaultKubernetesUserAgent()
 | 
							config.UserAgent = DefaultKubernetesUserAgent()
 | 
				
			||||||
@@ -398,12 +406,12 @@ func RESTClientFor(config *Config) (*RESTClient, error) {
 | 
				
			|||||||
		return nil, fmt.Errorf("Codec is required when initializing a RESTClient")
 | 
							return nil, fmt.Errorf("Codec is required when initializing a RESTClient")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	baseURL, err := defaultServerUrlFor(config)
 | 
						baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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)
 | 
						transport, err := TransportFor(config)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -423,12 +431,12 @@ func UnversionedRESTClientFor(config *Config) (*RESTClient, error) {
 | 
				
			|||||||
		return nil, fmt.Errorf("Codec is required when initializing a RESTClient")
 | 
							return nil, fmt.Errorf("Codec is required when initializing a RESTClient")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	baseURL, err := defaultServerUrlFor(config)
 | 
						baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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)
 | 
						transport, err := TransportFor(config)
 | 
				
			||||||
	if err != nil {
 | 
						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
 | 
					// 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
 | 
					// to use with a Client at a given API version following the standard conventions for a
 | 
				
			||||||
// Kubernetes API.
 | 
					// 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 == "" {
 | 
						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
 | 
						base := host
 | 
				
			||||||
	hostURL, err := url.Parse(base)
 | 
						hostURL, err := url.Parse(base)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if hostURL.Scheme == "" {
 | 
						if hostURL.Scheme == "" {
 | 
				
			||||||
		scheme := "http://"
 | 
							scheme := "http://"
 | 
				
			||||||
@@ -460,32 +468,34 @@ func DefaultServerURL(host, prefix string, groupVersion unversioned.GroupVersion
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		hostURL, err = url.Parse(scheme + base)
 | 
							hostURL, err = url.Parse(scheme + base)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, "", err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if hostURL.Path != "" && hostURL.Path != "/" {
 | 
							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
 | 
						// hostURL.Path is optional; a non-empty Path is treated as a prefix that is to be applied to
 | 
				
			||||||
	// append the default prefix
 | 
						// all URIs used to access the host. this is useful when there's a proxy in front of the
 | 
				
			||||||
	if hostURL.Path == "" {
 | 
						// apiserver that has relocated the apiserver endpoints, forwarding all requests from, for
 | 
				
			||||||
		if prefix == "" {
 | 
						// example, /a/b/c to the apiserver. in this case the Path should be /a/b/c.
 | 
				
			||||||
			prefix = "/"
 | 
						//
 | 
				
			||||||
		}
 | 
						// if running without a frontend proxy (that changes the location of the apiserver), then
 | 
				
			||||||
		hostURL.Path = prefix
 | 
						// 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
 | 
						// Add the version to the end of the path
 | 
				
			||||||
	if len(groupVersion.Group) > 0 {
 | 
						if len(groupVersion.Group) > 0 {
 | 
				
			||||||
		hostURL.Path = path.Join(hostURL.Path, groupVersion.Group, groupVersion.Version)
 | 
							versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Group, groupVersion.Version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	} else {
 | 
						} 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
 | 
					// 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
 | 
						// modify the copy of the config we got to satisfy preconditions for defaultServerUrlFor
 | 
				
			||||||
	config.GroupVersion = defaultVersionFor(&config)
 | 
						config.GroupVersion = defaultVersionFor(&config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	baseURL, err := defaultServerUrlFor(&config)
 | 
						baseURL, _, err := defaultServerUrlFor(&config)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -508,7 +518,7 @@ func IsConfigTransportTLS(config Config) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It
 | 
					// defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It
 | 
				
			||||||
// requires Host and Version to be set prior to being called.
 | 
					// 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
 | 
						// 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."
 | 
						// 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
 | 
						hasCA := len(config.CAFile) != 0 || len(config.CAData) != 0
 | 
				
			||||||
@@ -520,12 +530,12 @@ func defaultServerUrlFor(config *Config) (*url.URL, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if config.GroupVersion != nil {
 | 
						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 {
 | 
					func defaultVersionFor(config *Config) *unversioned.GroupVersion {
 | 
				
			||||||
	if config.GroupVersion == nil {
 | 
						if config.GroupVersion == nil {
 | 
				
			||||||
		// Clients default to the preferred code API version
 | 
							// Clients default to the preferred code API version
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import (
 | 
				
			|||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/http/httptest"
 | 
						"net/http/httptest"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
@@ -91,7 +92,7 @@ func TestSetKubernetesDefaults(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			Config{},
 | 
								Config{},
 | 
				
			||||||
			Config{
 | 
								Config{
 | 
				
			||||||
				Prefix:       "/api",
 | 
									APIPath:      "/api",
 | 
				
			||||||
				GroupVersion: testapi.Default.GroupVersion(),
 | 
									GroupVersion: testapi.Default.GroupVersion(),
 | 
				
			||||||
				Codec:        testapi.Default.Codec(),
 | 
									Codec:        testapi.Default.Codec(),
 | 
				
			||||||
				QPS:          5,
 | 
									QPS:          5,
 | 
				
			||||||
@@ -200,7 +201,7 @@ func TestSetsCodec(t *testing.T) {
 | 
				
			|||||||
		Prefix string
 | 
							Prefix string
 | 
				
			||||||
		Codec  runtime.Codec
 | 
							Codec  runtime.Codec
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		testapi.Default.GroupVersion().Version: {false, "/api/" + testapi.Default.GroupVersion().Version + "/", testapi.Default.Codec()},
 | 
							testapi.Default.GroupVersion().Version: {false, "/api/" + testapi.Default.GroupVersion().Version, testapi.Default.Codec()},
 | 
				
			||||||
		// Add this test back when we fixed config and SetKubernetesDefaults
 | 
							// Add this test back when we fixed config and SetKubernetesDefaults
 | 
				
			||||||
		// "invalidVersion":                       {true, "", nil},
 | 
							// "invalidVersion":                       {true, "", nil},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -216,7 +217,7 @@ func TestSetsCodec(t *testing.T) {
 | 
				
			|||||||
		case err != nil:
 | 
							case err != nil:
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if e, a := expected.Prefix, client.RESTClient.baseURL.Path; e != a {
 | 
							if e, a := expected.Prefix, client.RESTClient.versionedAPIPath; e != a {
 | 
				
			||||||
			t.Errorf("expected %#v, got %#v", e, a)
 | 
								t.Errorf("expected %#v, got %#v", e, a)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if e, a := expected.Codec, client.RESTClient.Codec; e != a {
 | 
							if e, a := expected.Codec, client.RESTClient.Codec; e != a {
 | 
				
			||||||
@@ -239,8 +240,8 @@ func TestRESTClientRequires(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestValidatesHostParameter(t *testing.T) {
 | 
					func TestValidatesHostParameter(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		Host   string
 | 
							Host    string
 | 
				
			||||||
		Prefix string
 | 
							APIPath string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		URL string
 | 
							URL string
 | 
				
			||||||
		Err bool
 | 
							Err bool
 | 
				
			||||||
@@ -255,7 +256,7 @@ func TestValidatesHostParameter(t *testing.T) {
 | 
				
			|||||||
		{"host/server", "", "", true},
 | 
							{"host/server", "", "", true},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for i, testCase := range testCases {
 | 
						for i, testCase := range testCases {
 | 
				
			||||||
		u, err := DefaultServerURL(testCase.Host, testCase.Prefix, *testapi.Default.GroupVersion(), false)
 | 
							u, versionedAPIPath, err := DefaultServerURL(testCase.Host, testCase.APIPath, *testapi.Default.GroupVersion(), false)
 | 
				
			||||||
		switch {
 | 
							switch {
 | 
				
			||||||
		case err == nil && testCase.Err:
 | 
							case err == nil && testCase.Err:
 | 
				
			||||||
			t.Errorf("expected error but was nil")
 | 
								t.Errorf("expected error but was nil")
 | 
				
			||||||
@@ -266,6 +267,7 @@ func TestValidatesHostParameter(t *testing.T) {
 | 
				
			|||||||
		case err != nil:
 | 
							case err != nil:
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							u.Path = path.Join(u.Path, versionedAPIPath)
 | 
				
			||||||
		if e, a := testCase.URL, u.String(); e != a {
 | 
							if e, a := testCase.URL, u.String(); e != a {
 | 
				
			||||||
			t.Errorf("%d: expected host %s, got %s", i, e, a)
 | 
								t.Errorf("%d: expected host %s, got %s", i, e, a)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -190,7 +190,7 @@ func TestRequestExecuteRemoteCommand(t *testing.T) {
 | 
				
			|||||||
		server := httptest.NewServer(fakeExecServer(t, i, testCase.Stdin, testCase.Stdout, testCase.Stderr, testCase.Error, testCase.Tty, testCase.MessageCount))
 | 
							server := httptest.NewServer(fakeExecServer(t, i, testCase.Stdin, testCase.Stdout, testCase.Stderr, testCase.Error, testCase.Tty, testCase.MessageCount))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		url, _ := url.ParseRequestURI(server.URL)
 | 
							url, _ := url.ParseRequestURI(server.URL)
 | 
				
			||||||
		c := client.NewRESTClient(url, unversioned.GroupVersion{Group: "x"}, nil, -1, -1)
 | 
							c := client.NewRESTClient(url, "", unversioned.GroupVersion{Group: "x"}, nil, -1, -1)
 | 
				
			||||||
		req := c.Post().Resource("testing")
 | 
							req := c.Post().Resource("testing")
 | 
				
			||||||
		req.SetHeader(httpstream.HeaderProtocolVersion, StreamProtocolV2Name)
 | 
							req.SetHeader(httpstream.HeaderProtocolVersion, StreamProtocolV2Name)
 | 
				
			||||||
		req.Param("command", "ls")
 | 
							req.Param("command", "ls")
 | 
				
			||||||
@@ -272,7 +272,7 @@ func TestRequestAttachRemoteCommand(t *testing.T) {
 | 
				
			|||||||
		server := httptest.NewServer(fakeExecServer(t, i, testCase.Stdin, testCase.Stdout, testCase.Stderr, testCase.Error, testCase.Tty, 1))
 | 
							server := httptest.NewServer(fakeExecServer(t, i, testCase.Stdin, testCase.Stdout, testCase.Stderr, testCase.Error, testCase.Tty, 1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		url, _ := url.ParseRequestURI(server.URL)
 | 
							url, _ := url.ParseRequestURI(server.URL)
 | 
				
			||||||
		c := client.NewRESTClient(url, unversioned.GroupVersion{Group: "x"}, nil, -1, -1)
 | 
							c := client.NewRESTClient(url, "", unversioned.GroupVersion{Group: "x"}, nil, -1, -1)
 | 
				
			||||||
		req := c.Post().Resource("testing")
 | 
							req := c.Post().Resource("testing")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		conf := &client.Config{
 | 
							conf := &client.Config{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,10 +87,10 @@ type Request struct {
 | 
				
			|||||||
	codec   runtime.Codec
 | 
						codec   runtime.Codec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// generic components accessible via method setters
 | 
						// generic components accessible via method setters
 | 
				
			||||||
	path    string
 | 
						pathPrefix string
 | 
				
			||||||
	subpath string
 | 
						subpath    string
 | 
				
			||||||
	params  url.Values
 | 
						params     url.Values
 | 
				
			||||||
	headers http.Header
 | 
						headers    http.Header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// structural elements of the request that are part of the Kubernetes API conventions
 | 
						// structural elements of the request that are part of the Kubernetes API conventions
 | 
				
			||||||
	namespace    string
 | 
						namespace    string
 | 
				
			||||||
@@ -115,17 +115,22 @@ type Request struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewRequest creates a new request helper object for accessing runtime.Objects on a server.
 | 
					// NewRequest creates a new request helper object for accessing runtime.Objects on a server.
 | 
				
			||||||
func NewRequest(client HTTPClient, verb string, baseURL *url.URL, groupVersion unversioned.GroupVersion, codec runtime.Codec, backoff BackoffManager) *Request {
 | 
					func NewRequest(client HTTPClient, verb string, baseURL *url.URL, versionedAPIPath string, groupVersion unversioned.GroupVersion, codec runtime.Codec, backoff BackoffManager) *Request {
 | 
				
			||||||
	if backoff == nil {
 | 
						if backoff == nil {
 | 
				
			||||||
		glog.V(2).Infof("Not implementing request backoff strategy.")
 | 
							glog.V(2).Infof("Not implementing request backoff strategy.")
 | 
				
			||||||
		backoff = &NoBackoff{}
 | 
							backoff = &NoBackoff{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	metrics.Register()
 | 
						metrics.Register()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pathPrefix := "/"
 | 
				
			||||||
 | 
						if baseURL != nil {
 | 
				
			||||||
 | 
							pathPrefix = path.Join(pathPrefix, baseURL.Path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return &Request{
 | 
						return &Request{
 | 
				
			||||||
		client:       client,
 | 
							client:       client,
 | 
				
			||||||
		verb:         verb,
 | 
							verb:         verb,
 | 
				
			||||||
		baseURL:      baseURL,
 | 
							baseURL:      baseURL,
 | 
				
			||||||
		path:         baseURL.Path,
 | 
							pathPrefix:   path.Join(pathPrefix, versionedAPIPath),
 | 
				
			||||||
		groupVersion: groupVersion,
 | 
							groupVersion: groupVersion,
 | 
				
			||||||
		codec:        codec,
 | 
							codec:        codec,
 | 
				
			||||||
		backoffMgr:   backoff,
 | 
							backoffMgr:   backoff,
 | 
				
			||||||
@@ -139,7 +144,7 @@ func (r *Request) Prefix(segments ...string) *Request {
 | 
				
			|||||||
	if r.err != nil {
 | 
						if r.err != nil {
 | 
				
			||||||
		return r
 | 
							return r
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	r.path = path.Join(r.path, path.Join(segments...))
 | 
						r.pathPrefix = path.Join(r.pathPrefix, path.Join(segments...))
 | 
				
			||||||
	return r
 | 
						return r
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -244,11 +249,10 @@ func (r *Request) AbsPath(segments ...string) *Request {
 | 
				
			|||||||
	if r.err != nil {
 | 
						if r.err != nil {
 | 
				
			||||||
		return r
 | 
							return r
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(segments) == 1 {
 | 
						r.pathPrefix = path.Join(r.baseURL.Path, path.Join(segments...))
 | 
				
			||||||
 | 
						if len(segments) == 1 && (len(r.baseURL.Path) > 1 || len(segments[0]) > 1) && strings.HasSuffix(segments[0], "/") {
 | 
				
			||||||
		// preserve any trailing slashes for legacy behavior
 | 
							// preserve any trailing slashes for legacy behavior
 | 
				
			||||||
		r.path = segments[0]
 | 
							r.pathPrefix += "/"
 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		r.path = path.Join(segments...)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return r
 | 
						return r
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -264,7 +268,7 @@ func (r *Request) RequestURI(uri string) *Request {
 | 
				
			|||||||
		r.err = err
 | 
							r.err = err
 | 
				
			||||||
		return r
 | 
							return r
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	r.path = locator.Path
 | 
						r.pathPrefix = locator.Path
 | 
				
			||||||
	if len(locator.Query()) > 0 {
 | 
						if len(locator.Query()) > 0 {
 | 
				
			||||||
		if r.params == nil {
 | 
							if r.params == nil {
 | 
				
			||||||
			r.params = make(url.Values)
 | 
								r.params = make(url.Values)
 | 
				
			||||||
@@ -554,14 +558,14 @@ func (r *Request) Body(obj interface{}) *Request {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// URL returns the current working URL.
 | 
					// URL returns the current working URL.
 | 
				
			||||||
func (r *Request) URL() *url.URL {
 | 
					func (r *Request) URL() *url.URL {
 | 
				
			||||||
	p := r.path
 | 
						p := r.pathPrefix
 | 
				
			||||||
	if r.namespaceSet && len(r.namespace) > 0 {
 | 
						if r.namespaceSet && len(r.namespace) > 0 {
 | 
				
			||||||
		p = path.Join(p, "namespaces", r.namespace)
 | 
							p = path.Join(p, "namespaces", r.namespace)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(r.resource) != 0 {
 | 
						if len(r.resource) != 0 {
 | 
				
			||||||
		p = path.Join(p, strings.ToLower(r.resource))
 | 
							p = path.Join(p, strings.ToLower(r.resource))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Join trims trailing slashes, so preserve r.path's trailing slash for backwards compat if nothing was changed
 | 
						// Join trims trailing slashes, so preserve r.pathPrefix's trailing slash for backwards compat if nothing was changed
 | 
				
			||||||
	if len(r.resourceName) != 0 || len(r.subpath) != 0 || len(r.subresource) != 0 {
 | 
						if len(r.resourceName) != 0 || len(r.subpath) != 0 || len(r.subresource) != 0 {
 | 
				
			||||||
		p = path.Join(p, r.resourceName, r.subresource, r.subpath)
 | 
							p = path.Join(p, r.resourceName, r.subresource, r.subpath)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,7 +70,7 @@ func TestRequestWithErrorWontChange(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRequestPreservesBaseTrailingSlash(t *testing.T) {
 | 
					func TestRequestPreservesBaseTrailingSlash(t *testing.T) {
 | 
				
			||||||
	r := &Request{baseURL: &url.URL{}, path: "/path/"}
 | 
						r := &Request{baseURL: &url.URL{}, pathPrefix: "/path/"}
 | 
				
			||||||
	if s := r.URL().String(); s != "/path/" {
 | 
						if s := r.URL().String(); s != "/path/" {
 | 
				
			||||||
		t.Errorf("trailing slash should be preserved: %s", s)
 | 
							t.Errorf("trailing slash should be preserved: %s", s)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -112,8 +112,8 @@ func TestRequestSetsNamespace(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestRequestOrdersNamespaceInPath(t *testing.T) {
 | 
					func TestRequestOrdersNamespaceInPath(t *testing.T) {
 | 
				
			||||||
	r := (&Request{
 | 
						r := (&Request{
 | 
				
			||||||
		baseURL: &url.URL{},
 | 
							baseURL:    &url.URL{},
 | 
				
			||||||
		path:    "/test/",
 | 
							pathPrefix: "/test/",
 | 
				
			||||||
	}).Name("bar").Resource("baz").Namespace("foo")
 | 
						}).Name("bar").Resource("baz").Namespace("foo")
 | 
				
			||||||
	if s := r.URL().String(); s != "/test/namespaces/foo/baz/bar" {
 | 
						if s := r.URL().String(); s != "/test/namespaces/foo/baz/bar" {
 | 
				
			||||||
		t.Errorf("namespace should be in order in path: %s", s)
 | 
							t.Errorf("namespace should be in order in path: %s", s)
 | 
				
			||||||
@@ -122,8 +122,8 @@ func TestRequestOrdersNamespaceInPath(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestRequestOrdersSubResource(t *testing.T) {
 | 
					func TestRequestOrdersSubResource(t *testing.T) {
 | 
				
			||||||
	r := (&Request{
 | 
						r := (&Request{
 | 
				
			||||||
		baseURL: &url.URL{},
 | 
							baseURL:    &url.URL{},
 | 
				
			||||||
		path:    "/test/",
 | 
							pathPrefix: "/test/",
 | 
				
			||||||
	}).Name("bar").Resource("baz").Namespace("foo").Suffix("test").SubResource("a", "b")
 | 
						}).Name("bar").Resource("baz").Namespace("foo").Suffix("test").SubResource("a", "b")
 | 
				
			||||||
	if s := r.URL().String(); s != "/test/namespaces/foo/baz/bar/a/b/test" {
 | 
						if s := r.URL().String(); s != "/test/namespaces/foo/baz/bar/a/b/test" {
 | 
				
			||||||
		t.Errorf("namespace should be in order in path: %s", s)
 | 
							t.Errorf("namespace should be in order in path: %s", s)
 | 
				
			||||||
@@ -216,7 +216,7 @@ func TestRequestURI(t *testing.T) {
 | 
				
			|||||||
	r := (&Request{}).Param("foo", "a")
 | 
						r := (&Request{}).Param("foo", "a")
 | 
				
			||||||
	r.Prefix("other")
 | 
						r.Prefix("other")
 | 
				
			||||||
	r.RequestURI("/test?foo=b&a=b&c=1&c=2")
 | 
						r.RequestURI("/test?foo=b&a=b&c=1&c=2")
 | 
				
			||||||
	if r.path != "/test" {
 | 
						if r.pathPrefix != "/test" {
 | 
				
			||||||
		t.Errorf("path is wrong: %#v", r)
 | 
							t.Errorf("path is wrong: %#v", r)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !reflect.DeepEqual(r.params, url.Values{"a": []string{"b"}, "foo": []string{"b"}, "c": []string{"1", "2"}}) {
 | 
						if !reflect.DeepEqual(r.params, url.Values{"a": []string{"b"}, "foo": []string{"b"}, "c": []string{"1", "2"}}) {
 | 
				
			||||||
@@ -263,7 +263,7 @@ func TestResultIntoWithErrReturnsErr(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestURLTemplate(t *testing.T) {
 | 
					func TestURLTemplate(t *testing.T) {
 | 
				
			||||||
	uri, _ := url.Parse("http://localhost")
 | 
						uri, _ := url.Parse("http://localhost")
 | 
				
			||||||
	r := NewRequest(nil, "POST", uri, unversioned.GroupVersion{Group: "test"}, nil, nil)
 | 
						r := NewRequest(nil, "POST", uri, "", unversioned.GroupVersion{Group: "test"}, nil, nil)
 | 
				
			||||||
	r.Prefix("pre1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0")
 | 
						r.Prefix("pre1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0")
 | 
				
			||||||
	full := r.URL()
 | 
						full := r.URL()
 | 
				
			||||||
	if full.String() != "http://localhost/pre1/namespaces/ns/r1/nm?p0=v0" {
 | 
						if full.String() != "http://localhost/pre1/namespaces/ns/r1/nm?p0=v0" {
 | 
				
			||||||
@@ -324,7 +324,7 @@ func TestTransformResponse(t *testing.T) {
 | 
				
			|||||||
		{Response: &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
 | 
							{Response: &http.Response{StatusCode: 200, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for i, test := range testCases {
 | 
						for i, test := range testCases {
 | 
				
			||||||
		r := NewRequest(nil, "", uri, *testapi.Default.GroupVersion(), testapi.Default.Codec(), nil)
 | 
							r := NewRequest(nil, "", uri, "", *testapi.Default.GroupVersion(), testapi.Default.Codec(), nil)
 | 
				
			||||||
		if test.Response.Body == nil {
 | 
							if test.Response.Body == nil {
 | 
				
			||||||
			test.Response.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
 | 
								test.Response.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -448,7 +448,7 @@ func TestRequestWatch(t *testing.T) {
 | 
				
			|||||||
			Err:     true,
 | 
								Err:     true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Request: &Request{baseURL: &url.URL{}, path: "%"},
 | 
								Request: &Request{baseURL: &url.URL{}, pathPrefix: "%"},
 | 
				
			||||||
			Err:     true,
 | 
								Err:     true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -577,7 +577,7 @@ func TestRequestStream(t *testing.T) {
 | 
				
			|||||||
			Err:     true,
 | 
								Err:     true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Request: &Request{baseURL: &url.URL{}, path: "%"},
 | 
								Request: &Request{baseURL: &url.URL{}, pathPrefix: "%"},
 | 
				
			||||||
			Err:     true,
 | 
								Err:     true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -663,7 +663,7 @@ func TestRequestDo(t *testing.T) {
 | 
				
			|||||||
			Err:     true,
 | 
								Err:     true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Request: &Request{baseURL: &url.URL{}, path: "%"},
 | 
								Request: &Request{baseURL: &url.URL{}, pathPrefix: "%"},
 | 
				
			||||||
			Err:     true,
 | 
								Err:     true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -1050,11 +1050,37 @@ func TestVerbs(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAbsPath(t *testing.T) {
 | 
					func TestAbsPath(t *testing.T) {
 | 
				
			||||||
	expectedPath := "/bar/foo"
 | 
						for i, tc := range []struct {
 | 
				
			||||||
	c := testRESTClient(t, nil)
 | 
							configPrefix   string
 | 
				
			||||||
	r := c.Post().Prefix("/foo").AbsPath(expectedPath)
 | 
							resourcePrefix string
 | 
				
			||||||
	if r.path != expectedPath {
 | 
							absPath        string
 | 
				
			||||||
		t.Errorf("unexpected path: %s, expected %s", r.path, expectedPath)
 | 
							wantsAbsPath   string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{"", "", "", "/"},
 | 
				
			||||||
 | 
							{"", "", "/", "/"},
 | 
				
			||||||
 | 
							{"", "", "/api", "/api"},
 | 
				
			||||||
 | 
							{"", "", "/api/", "/api/"},
 | 
				
			||||||
 | 
							{"", "", "/apis", "/apis"},
 | 
				
			||||||
 | 
							{"", "/foo", "/bar/foo", "/bar/foo"},
 | 
				
			||||||
 | 
							{"", "/api/foo/123", "/bar/foo", "/bar/foo"},
 | 
				
			||||||
 | 
							{"/p1", "", "", "/p1"},
 | 
				
			||||||
 | 
							{"/p1", "", "/", "/p1/"},
 | 
				
			||||||
 | 
							{"/p1", "", "/api", "/p1/api"},
 | 
				
			||||||
 | 
							{"/p1", "", "/apis", "/p1/apis"},
 | 
				
			||||||
 | 
							{"/p1", "/r1", "/apis", "/p1/apis"},
 | 
				
			||||||
 | 
							{"/p1", "/api/r1", "/apis", "/p1/apis"},
 | 
				
			||||||
 | 
							{"/p1/api/p2", "", "", "/p1/api/p2"},
 | 
				
			||||||
 | 
							{"/p1/api/p2", "", "/", "/p1/api/p2/"},
 | 
				
			||||||
 | 
							{"/p1/api/p2", "", "/api", "/p1/api/p2/api"},
 | 
				
			||||||
 | 
							{"/p1/api/p2", "", "/api/", "/p1/api/p2/api/"},
 | 
				
			||||||
 | 
							{"/p1/api/p2", "/r1", "/api/", "/p1/api/p2/api/"},
 | 
				
			||||||
 | 
							{"/p1/api/p2", "/api/r1", "/api/", "/p1/api/p2/api/"},
 | 
				
			||||||
 | 
						} {
 | 
				
			||||||
 | 
							c := NewOrDie(&Config{Host: "http://localhost:123" + tc.configPrefix})
 | 
				
			||||||
 | 
							r := c.Post().Prefix(tc.resourcePrefix).AbsPath(tc.absPath)
 | 
				
			||||||
 | 
							if r.pathPrefix != tc.wantsAbsPath {
 | 
				
			||||||
 | 
								t.Errorf("test case %d failed, unexpected path: %q, expected %q", i, r.pathPrefix, tc.wantsAbsPath)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1071,7 +1097,7 @@ func TestUintParam(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, item := range table {
 | 
						for _, item := range table {
 | 
				
			||||||
		u, _ := url.Parse("http://localhost")
 | 
							u, _ := url.Parse("http://localhost")
 | 
				
			||||||
		r := NewRequest(nil, "GET", u, unversioned.GroupVersion{Group: "test"}, nil, nil).AbsPath("").UintParam(item.name, item.testVal)
 | 
							r := NewRequest(nil, "GET", u, "", unversioned.GroupVersion{Group: "test"}, nil, nil).AbsPath("").UintParam(item.name, item.testVal)
 | 
				
			||||||
		if e, a := item.expectStr, r.URL().String(); e != a {
 | 
							if e, a := item.expectStr, r.URL().String(); e != a {
 | 
				
			||||||
			t.Errorf("expected %v, got %v", e, a)
 | 
								t.Errorf("expected %v, got %v", e, a)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -1241,6 +1267,6 @@ func testRESTClient(t testing.TB, srv *httptest.Server) *RESTClient {
 | 
				
			|||||||
			t.Fatalf("failed to parse test URL: %v", err)
 | 
								t.Fatalf("failed to parse test URL: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	baseURL.Path = testapi.Default.ResourcePath("", "", "")
 | 
						versionedAPIPath := testapi.Default.ResourcePath("", "", "")
 | 
				
			||||||
	return NewRESTClient(baseURL, *testapi.Default.GroupVersion(), testapi.Default.Codec(), 0, 0)
 | 
						return NewRESTClient(baseURL, versionedAPIPath, *testapi.Default.GroupVersion(), testapi.Default.Codec(), 0, 0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,7 +45,8 @@ const (
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// Most consumers should use client.New() to get a Kubernetes API client.
 | 
					// Most consumers should use client.New() to get a Kubernetes API client.
 | 
				
			||||||
type RESTClient struct {
 | 
					type RESTClient struct {
 | 
				
			||||||
	baseURL *url.URL
 | 
						baseURL          *url.URL
 | 
				
			||||||
 | 
						versionedAPIPath string
 | 
				
			||||||
	// A string identifying the version of the API this client is expected to use.
 | 
						// A string identifying the version of the API this client is expected to use.
 | 
				
			||||||
	groupVersion unversioned.GroupVersion
 | 
						groupVersion unversioned.GroupVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,7 +65,7 @@ type RESTClient struct {
 | 
				
			|||||||
// NewRESTClient creates a new RESTClient. This client performs generic REST functions
 | 
					// NewRESTClient creates a new RESTClient. This client performs generic REST functions
 | 
				
			||||||
// such as Get, Put, Post, and Delete on specified paths.  Codec controls encoding and
 | 
					// such as Get, Put, Post, and Delete on specified paths.  Codec controls encoding and
 | 
				
			||||||
// decoding of responses from the server.
 | 
					// decoding of responses from the server.
 | 
				
			||||||
func NewRESTClient(baseURL *url.URL, groupVersion unversioned.GroupVersion, c runtime.Codec, maxQPS float32, maxBurst int) *RESTClient {
 | 
					func NewRESTClient(baseURL *url.URL, versionedAPIPath string, groupVersion unversioned.GroupVersion, c runtime.Codec, maxQPS float32, maxBurst int) *RESTClient {
 | 
				
			||||||
	base := *baseURL
 | 
						base := *baseURL
 | 
				
			||||||
	if !strings.HasSuffix(base.Path, "/") {
 | 
						if !strings.HasSuffix(base.Path, "/") {
 | 
				
			||||||
		base.Path += "/"
 | 
							base.Path += "/"
 | 
				
			||||||
@@ -77,10 +78,11 @@ func NewRESTClient(baseURL *url.URL, groupVersion unversioned.GroupVersion, c ru
 | 
				
			|||||||
		throttle = util.NewTokenBucketRateLimiter(maxQPS, maxBurst)
 | 
							throttle = util.NewTokenBucketRateLimiter(maxQPS, maxBurst)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &RESTClient{
 | 
						return &RESTClient{
 | 
				
			||||||
		baseURL:      &base,
 | 
							baseURL:          &base,
 | 
				
			||||||
		groupVersion: groupVersion,
 | 
							versionedAPIPath: versionedAPIPath,
 | 
				
			||||||
		Codec:        c,
 | 
							groupVersion:     groupVersion,
 | 
				
			||||||
		Throttle:     throttle,
 | 
							Codec:            c,
 | 
				
			||||||
 | 
							Throttle:         throttle,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -123,9 +125,9 @@ func (c *RESTClient) Verb(verb string) *Request {
 | 
				
			|||||||
	backoff := readExpBackoffConfig()
 | 
						backoff := readExpBackoffConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.Client == nil {
 | 
						if c.Client == nil {
 | 
				
			||||||
		return NewRequest(nil, verb, c.baseURL, c.groupVersion, c.Codec, backoff)
 | 
							return NewRequest(nil, verb, c.baseURL, c.versionedAPIPath, c.groupVersion, c.Codec, backoff)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return NewRequest(c.Client, verb, c.baseURL, c.groupVersion, c.Codec, backoff)
 | 
						return NewRequest(c.Client, verb, c.baseURL, c.versionedAPIPath, c.groupVersion, c.Codec, backoff)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Post begins a POST request. Short for c.Verb("POST").
 | 
					// Post begins a POST request. Short for c.Verb("POST").
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,14 +134,10 @@ func TestSetWithPathPrefixIntoExistingStruct(t *testing.T) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("unexpected error: %v", err)
 | 
							t.Fatalf("unexpected error: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	expectedHost := "http://cow.org:8080"
 | 
						expectedHost := "http://cow.org:8080/foo/baz"
 | 
				
			||||||
	if expectedHost != dcc.Host {
 | 
						if expectedHost != dcc.Host {
 | 
				
			||||||
		t.Fatalf("expected client.Config.Host = %q instead of %q", expectedHost, dcc.Host)
 | 
							t.Fatalf("expected client.Config.Host = %q instead of %q", expectedHost, dcc.Host)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	expectedPrefix := "/foo/baz"
 | 
					 | 
				
			||||||
	if expectedPrefix != dcc.Prefix {
 | 
					 | 
				
			||||||
		t.Fatalf("expected client.Config.Prefix = %q instead of %q", expectedPrefix, dcc.Prefix)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestUnsetStruct(t *testing.T) {
 | 
					func TestUnsetStruct(t *testing.T) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -203,7 +203,6 @@ var _ = Describe("Kubectl client", func() {
 | 
				
			|||||||
				Failf("Unable to parse URL %s. Error=%s", apiServer, err)
 | 
									Failf("Unable to parse URL %s. Error=%s", apiServer, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			apiServerUrl.Scheme = "https"
 | 
								apiServerUrl.Scheme = "https"
 | 
				
			||||||
			apiServerUrl.Path = "/api"
 | 
					 | 
				
			||||||
			if !strings.Contains(apiServer, ":443") {
 | 
								if !strings.Contains(apiServer, ":443") {
 | 
				
			||||||
				apiServerUrl.Host = apiServerUrl.Host + ":443"
 | 
									apiServerUrl.Host = apiServerUrl.Host + ":443"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user