diff --git a/contrib/prometheus/prometheus-all.json b/contrib/prometheus/prometheus-all.json index a751b72519a..9a90add1f47 100644 --- a/contrib/prometheus/prometheus-all.json +++ b/contrib/prometheus/prometheus-all.json @@ -73,9 +73,9 @@ }, { "name": "kubectl", - "image": "gcr.io/google_containers/kubectl:v0.18.0-120-gaeb4ac55ad12b1-dirty", + "image": "gcr.io/google_containers/kubectl:v0.18.0-350-gfb3305edcf6c1a", "args": [ - "proxy", "-p", "8001" + "proxy", "-p", "8001", "--api-prefix=/" ] } ], diff --git a/docs/kubectl_proxy.md b/docs/kubectl_proxy.md index ea38efe158a..192a6c5ecb4 100644 --- a/docs/kubectl_proxy.md +++ b/docs/kubectl_proxy.md @@ -5,7 +5,22 @@ Run a proxy to the Kubernetes API server ### Synopsis -Run a proxy to the Kubernetes API server. +To proxy all of the kubernetes api and nothing else, use: + +kubectl proxy --api-prefix=/ + +To proxy only part of the kubernetes api and also some static files: + +kubectl proxy --www=/my/files --www-prefix=/static/ --api-prefix=/api/ + +The above lets you 'curl localhost:8001/api/v1/pods'. + +To proxy the entire kubernetes api at a different root, use: + +kubectl proxy --api-prefix=/custom/ + +The above lets you 'curl localhost:8001/custom/api/v1/pods' + ``` kubectl proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix] @@ -64,6 +79,6 @@ $ kubectl proxy --api-prefix=/k8s-api ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-05-28 20:57:46.689818993 +0000 UTC +###### Auto generated by spf13/cobra at 2015-06-04 01:34:05.594492715 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl_proxy.md?pixel)]() diff --git a/docs/man/man1/kubectl-proxy.1 b/docs/man/man1/kubectl-proxy.1 index 0875897c694..ba67f875ba9 100644 --- a/docs/man/man1/kubectl-proxy.1 +++ b/docs/man/man1/kubectl-proxy.1 @@ -13,7 +13,28 @@ kubectl proxy \- Run a proxy to the Kubernetes API server .SH DESCRIPTION .PP -Run a proxy to the Kubernetes API server. +To proxy all of the kubernetes api and nothing else, use: + +.PP +kubectl proxy \-\-api\-prefix=/ + +.PP +To proxy only part of the kubernetes api and also some static files: + +.PP +kubectl proxy \-\-www=/my/files \-\-www\-prefix=/static/ \-\-api\-prefix=/api/ + +.PP +The above lets you 'curl localhost:8001/api/v1/pods'. + +.PP +To proxy the entire kubernetes api at a different root, use: + +.PP +kubectl proxy \-\-api\-prefix=/custom/ + +.PP +The above lets you 'curl localhost:8001/custom/api/v1/pods' .SH OPTIONS diff --git a/pkg/kubectl/cmd/proxy.go b/pkg/kubectl/cmd/proxy.go index 234e0f5332b..5b607f4f383 100644 --- a/pkg/kubectl/cmd/proxy.go +++ b/pkg/kubectl/cmd/proxy.go @@ -38,9 +38,24 @@ $ kubectl proxy --api-prefix=/k8s-api` func NewCmdProxy(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix]", - Short: "Run a proxy to the Kubernetes API server", - Long: `Run a proxy to the Kubernetes API server. `, + Use: "proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix]", + Short: "Run a proxy to the Kubernetes API server", + Long: `To proxy all of the kubernetes api and nothing else, use: + +kubectl proxy --api-prefix=/ + +To proxy only part of the kubernetes api and also some static files: + +kubectl proxy --www=/my/files --www-prefix=/static/ --api-prefix=/api/ + +The above lets you 'curl localhost:8001/api/v1/pods'. + +To proxy the entire kubernetes api at a different root, use: + +kubectl proxy --api-prefix=/custom/ + +The above lets you 'curl localhost:8001/custom/api/v1/pods' +`, Example: proxy_example, Run: func(cmd *cobra.Command, args []string) { err := RunProxy(f, out, cmd) diff --git a/pkg/kubectl/proxy_server.go b/pkg/kubectl/proxy_server.go index f307c0f2b52..63f58c530c5 100644 --- a/pkg/kubectl/proxy_server.go +++ b/pkg/kubectl/proxy_server.go @@ -28,17 +28,18 @@ import ( // ProxyServer is a http.Handler which proxies Kubernetes APIs to remote API server. type ProxyServer struct { + mux *http.ServeMux httputil.ReverseProxy } // NewProxyServer creates and installs a new ProxyServer. // It automatically registers the created ProxyServer to http.DefaultServeMux. func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, cfg *client.Config) (*ProxyServer, error) { - prefix := cfg.Prefix - if prefix == "" { - prefix = "/api" + host := cfg.Host + if !strings.HasSuffix(host, "/") { + host = host + "/" } - target, err := url.Parse(singleJoiningSlash(cfg.Host, prefix)) + target, err := url.Parse(host) if err != nil { return nil, err } @@ -46,15 +47,22 @@ func NewProxyServer(filebase string, apiProxyPrefix string, staticPrefix string, if proxy.Transport, err = client.TransportFor(cfg); err != nil { return nil, err } - http.Handle(apiProxyPrefix, http.StripPrefix(apiProxyPrefix, proxy)) - http.Handle(staticPrefix, newFileHandler(staticPrefix, filebase)) + if strings.HasPrefix(apiProxyPrefix, "/api") { + proxy.mux.Handle(apiProxyPrefix, proxy) + } else { + proxy.mux.Handle(apiProxyPrefix, http.StripPrefix(apiProxyPrefix, proxy)) + } + proxy.mux.Handle(staticPrefix, newFileHandler(staticPrefix, filebase)) return proxy, nil } // Serve starts the server (http.DefaultServeMux) on given port, loops forever. func (s *ProxyServer) Serve(port int) error { - addr := fmt.Sprintf(":%d", port) - return http.ListenAndServe(addr, nil) + server := http.Server{ + Addr: fmt.Sprintf(":%d", port), + Handler: s.mux, + } + return server.ListenAndServe() } func newProxyServer(target *url.URL) *ProxyServer { @@ -63,7 +71,10 @@ func newProxyServer(target *url.URL) *ProxyServer { req.URL.Host = target.Host req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path) } - return &ProxyServer{ReverseProxy: httputil.ReverseProxy{Director: director}} + return &ProxyServer{ + ReverseProxy: httputil.ReverseProxy{Director: director}, + mux: http.NewServeMux(), + } } func newFileHandler(prefix, base string) http.Handler { diff --git a/pkg/kubectl/proxy_server_test.go b/pkg/kubectl/proxy_server_test.go index ad9fdd7a7e9..a8e9dc213de 100644 --- a/pkg/kubectl/proxy_server_test.go +++ b/pkg/kubectl/proxy_server_test.go @@ -25,6 +25,8 @@ import ( "path/filepath" "strings" "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/client" ) func TestFileServing(t *testing.T) { @@ -104,3 +106,55 @@ func TestAPIRequests(t *testing.T) { } } } + +func TestPathHandling(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, r.URL.Path) + })) + defer ts.Close() + + table := []struct { + prefix string + reqPath string + expectPath string + }{ + {"/api/", "/metrics", "404 page not found\n"}, + {"/api/", "/api/metrics", "/api/metrics"}, + {"/api/", "/api/v1/pods/", "/api/v1/pods/"}, + {"/", "/metrics", "/metrics"}, + {"/", "/api/v1/pods/", "/api/v1/pods/"}, + {"/custom/", "/metrics", "404 page not found\n"}, + {"/custom/", "/api/metrics", "404 page not found\n"}, + {"/custom/", "/api/v1/pods/", "404 page not found\n"}, + {"/custom/", "/custom/api/metrics", "/api/metrics"}, + {"/custom/", "/custom/api/v1/pods/", "/api/v1/pods/"}, + } + + cc := &client.Config{ + Host: ts.URL, + } + + for _, item := range table { + func() { + p, err := NewProxyServer("", item.prefix, "/not/used/for/this/test", cc) + if err != nil { + t.Fatalf("%#v: %v", item, err) + } + pts := httptest.NewServer(p.mux) + defer pts.Close() + + r, err := http.Get(pts.URL + item.reqPath) + if err != nil { + t.Fatalf("%#v: %v", item, err) + } + body, err := ioutil.ReadAll(r.Body) + r.Body.Close() + if err != nil { + t.Fatalf("%#v: %v", item, err) + } + if e, a := item.expectPath, string(body); e != a { + t.Errorf("%#v: Wanted %q, got %q", item, e, a) + } + }() + } +}