diff --git a/pkg/api/unversioned.go b/pkg/api/unversioned.go index 302c0eaa1a8..c1604b5612f 100644 --- a/pkg/api/unversioned.go +++ b/pkg/api/unversioned.go @@ -24,3 +24,9 @@ package api type APIVersions struct { Versions []string `json:"versions"` } + +// RootPaths lists the paths available at root. +// For example: "/healthz", "/api". +type RootPaths struct { + Paths []string `json:"paths"` +} diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index f8f5448c578..70eec069e37 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -62,9 +62,9 @@ func Handle(storage map[string]RESTStorage, codec runtime.Codec, root string, ve group := NewAPIGroupVersion(storage, codec, prefix, selfLinker, admissionControl) container := restful.NewContainer() mux := container.ServeMux - group.InstallREST(container, root, version) + group.InstallREST(container, mux, root, version) ws := new(restful.WebService) - InstallSupport(container, ws) + InstallSupport(mux, ws) container.Add(ws) return &defaultAPIServer{mux, group} } @@ -203,7 +203,7 @@ func addParamIf(b *restful.RouteBuilder, parameter *restful.Parameter, shouldAdd // InstallREST registers the REST handlers (storage, watch, and operations) into a restful Container. // It is expected that the provided path root prefix will serve all operations. Root MUST NOT end // in a slash. A restful WebService is created for the group and version. -func (g *APIGroupVersion) InstallREST(container *restful.Container, root string, version string) error { +func (g *APIGroupVersion) InstallREST(container *restful.Container, mux Mux, root string, version string) error { prefix := path.Join(root, version) restHandler := &g.handler strippedHandler := http.StripPrefix(prefix, restHandler) @@ -268,8 +268,6 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container, root string, // TODO: port the rest of these. Sadly, if we don't, we'll have inconsistent // API behavior, as well as lack of documentation - mux := container.ServeMux - // Note: update GetAttribs() when adding a handler. mux.Handle(prefix+"/watch/", http.StripPrefix(prefix+"/watch/", watchHandler)) mux.Handle(prefix+"/proxy/", http.StripPrefix(prefix+"/proxy/", proxyHandler)) @@ -296,9 +294,9 @@ func InstallValidator(mux Mux, servers func() map[string]Server) { // TODO: document all handlers // InstallSupport registers the APIServer support functions -func InstallSupport(container *restful.Container, ws *restful.WebService) { +func InstallSupport(mux Mux, ws *restful.WebService) { // TODO: convert healthz to restful and remove container arg - healthz.InstallHandler(container.ServeMux) + healthz.InstallHandler(mux) // Set up a service to return the git code version. ws.Path("/version") diff --git a/pkg/apiserver/index.go b/pkg/apiserver/index.go index 3b05c5270f2..0b61665a106 100644 --- a/pkg/apiserver/index.go +++ b/pkg/apiserver/index.go @@ -17,11 +17,24 @@ limitations under the License. package apiserver import ( - "io" "net/http" + "sort" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + + "github.com/emicklei/go-restful" ) -// handleIndex is the root index page for Kubernetes. -func HandleIndex(w http.ResponseWriter, r *http.Request) { - io.WriteString(w, "Welcome to Kubernetes") +func IndexHandler(container *restful.Container, muxHelper *MuxHelper) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var handledPaths []string + // Extract the paths handled using restful.WebService + for _, ws := range container.RegisteredWebServices() { + handledPaths = append(handledPaths, ws.RootPath()) + } + // Extract the paths handled using mux handler. + handledPaths = append(handledPaths, muxHelper.RegisteredPaths...) + sort.Strings(handledPaths) + writeRawJSON(http.StatusOK, api.RootPaths{Paths: handledPaths}, w) + } } diff --git a/pkg/apiserver/mux_helper.go b/pkg/apiserver/mux_helper.go new file mode 100644 index 00000000000..e95f0719cdb --- /dev/null +++ b/pkg/apiserver/mux_helper.go @@ -0,0 +1,37 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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. +*/ + +package apiserver + +import ( + "net/http" +) + +// Offers additional functionality over ServeMux, for ex: supports listing registered paths. +type MuxHelper struct { + Mux Mux + RegisteredPaths []string +} + +func (m *MuxHelper) Handle(path string, handler http.Handler) { + m.RegisteredPaths = append(m.RegisteredPaths, path) + m.Mux.Handle(path, handler) +} + +func (m *MuxHelper) HandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) { + m.RegisteredPaths = append(m.RegisteredPaths, path) + m.Mux.HandleFunc(path, handler) +} diff --git a/pkg/master/master.go b/pkg/master/master.go index 796dcf20393..be5ad949b41 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -114,6 +114,7 @@ type Master struct { portalNet *net.IPNet mux apiserver.Mux + muxHelper *apiserver.MuxHelper handlerContainer *restful.Container rootWebService *restful.WebService enableLogsSupport bool @@ -274,6 +275,7 @@ func New(c *Config) *Master { m.mux = mux m.handlerContainer = NewHandlerContainer(mux) } + m.muxHelper = &apiserver.MuxHelper{m.mux, []string{}} m.masterServices = util.NewRunner(m.serviceWriterLoop, m.roServiceWriterLoop) m.init(c) @@ -289,7 +291,7 @@ func (m *Master) HandleWithAuth(pattern string, handler http.Handler) { // sensible policy defaults for plugged-in endpoints. This will be different // for generic endpoints versus REST object endpoints. // TODO: convert to go-restful - m.mux.Handle(pattern, handler) + m.muxHelper.Handle(pattern, handler) } // HandleFuncWithAuth adds an http.Handler for pattern to an http.ServeMux @@ -297,7 +299,7 @@ func (m *Master) HandleWithAuth(pattern string, handler http.Handler) { // to the request is used for the master's built-in endpoints. func (m *Master) HandleFuncWithAuth(pattern string, handler func(http.ResponseWriter, *http.Request)) { // TODO: convert to go-restful - m.mux.HandleFunc(pattern, handler) + m.muxHelper.HandleFunc(pattern, handler) } func NewHandlerContainer(mux *http.ServeMux) *restful.Container { @@ -362,33 +364,33 @@ func (m *Master) init(c *Config) { } apiVersions := []string{"v1beta1", "v1beta2"} - if err := apiserver.NewAPIGroupVersion(m.api_v1beta1()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta1"); err != nil { + if err := apiserver.NewAPIGroupVersion(m.api_v1beta1()).InstallREST(m.handlerContainer, m.muxHelper, c.APIPrefix, "v1beta1"); err != nil { glog.Fatalf("Unable to setup API v1beta1: %v", err) } - if err := apiserver.NewAPIGroupVersion(m.api_v1beta2()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta2"); err != nil { + if err := apiserver.NewAPIGroupVersion(m.api_v1beta2()).InstallREST(m.handlerContainer, m.muxHelper, c.APIPrefix, "v1beta2"); err != nil { glog.Fatalf("Unable to setup API v1beta2: %v", err) } if c.EnableV1Beta3 { - if err := apiserver.NewAPIGroupVersion(m.api_v1beta3()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta3"); err != nil { + if err := apiserver.NewAPIGroupVersion(m.api_v1beta3()).InstallREST(m.handlerContainer, m.muxHelper, c.APIPrefix, "v1beta3"); err != nil { glog.Fatalf("Unable to setup API v1beta3: %v", err) } apiVersions = []string{"v1beta1", "v1beta2", "v1beta3"} } - apiserver.InstallSupport(m.handlerContainer, m.rootWebService) + apiserver.InstallSupport(m.muxHelper, m.rootWebService) apiserver.AddApiWebService(m.handlerContainer, c.APIPrefix, apiVersions) // Register root handler. // We do not register this using restful Webservice since we do not want to surface this in api docs. - m.mux.HandleFunc("/", apiserver.HandleIndex) + m.mux.HandleFunc("/", apiserver.IndexHandler(m.handlerContainer, m.muxHelper)) // TODO: use go-restful - apiserver.InstallValidator(m.mux, func() map[string]apiserver.Server { return m.getServersToValidate(c) }) + apiserver.InstallValidator(m.muxHelper, func() map[string]apiserver.Server { return m.getServersToValidate(c) }) if c.EnableLogsSupport { - apiserver.InstallLogsSupport(m.mux) + apiserver.InstallLogsSupport(m.muxHelper) } if c.EnableUISupport { - ui.InstallSupport(m.mux, m.enableSwaggerSupport) + ui.InstallSupport(m.muxHelper, m.enableSwaggerSupport) } // TODO: install runtime/pprof handler