diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 076f9ced270..f119a1352f3 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -56,6 +56,7 @@ const ( // Handle returns a Handler function that exposes the provided storage interfaces // as RESTful resources at prefix, serialized by codec, and also includes the support // http resources. +// Note: This method is used only in tests. func Handle(storage map[string]RESTStorage, codec runtime.Codec, root string, version string, selfLinker runtime.SelfLinker, admissionControl admission.Interface) http.Handler { prefix := root + "/" + version group := NewAPIGroupVersion(storage, codec, prefix, selfLinker, admissionControl) @@ -122,14 +123,19 @@ func registerResourceHandlers(ws *restful.WebService, version string, path strin // and status-code behavior if namespaceScope { path = "ns/{namespace}/" + path - ws.Param(ws.PathParameter("namespace", "object name and auth scope, such as for teams and projects").DataType("string")) } glog.V(3).Infof("Installing version=/%s, kind=/%s, path=/%s\n", version, kind, path) - ws.Route(ws.POST(path).To(h). - Doc("create a " + kind). - Operation("create" + kind). - Reads(versionedObject)) // from the request + nameParam := ws.PathParameter("name", "name of the "+kind).DataType("string") + namespaceParam := ws.PathParameter("namespace", "object name and auth scope, such as for teams and projects").DataType("string") + + ws.Route( + addParamIf( + ws.POST(path).To(h). + Doc("create a "+kind). + Operation("create"+kind). + Reads(versionedObject), // from the request + namespaceParam, namespaceScope)) // TODO: This seems like a hack. Add NewList() to storage? listKind := kind + "List" @@ -142,31 +148,51 @@ func registerResourceHandlers(ws *restful.WebService, version string, path strin } else { versionedList := indirectArbitraryPointer(versionedListPtr) glog.V(3).Infoln("type: ", reflect.TypeOf(versionedList)) - ws.Route(ws.GET(path).To(h). - Doc("list objects of kind "+kind). - Operation("list"+kind). - Returns(http.StatusOK, "OK", versionedList)) + ws.Route( + addParamIf( + ws.GET(path).To(h). + Doc("list objects of kind "+kind). + Operation("list"+kind). + Returns(http.StatusOK, "OK", versionedList), + namespaceParam, namespaceScope)) } } - ws.Route(ws.GET(path + "/{name}").To(h). - Doc("read the specified " + kind). - Operation("read" + kind). - Param(ws.PathParameter("name", "name of the "+kind).DataType("string")). - Writes(versionedObject)) // on the response + ws.Route( + addParamIf( + ws.GET(path+"/{name}").To(h). + Doc("read the specified "+kind). + Operation("read"+kind). + Param(nameParam). + Writes(versionedObject), // on the response + namespaceParam, namespaceScope)) - ws.Route(ws.PUT(path + "/{name}").To(h). - Doc("update the specified " + kind). - Operation("update" + kind). - Param(ws.PathParameter("name", "name of the "+kind).DataType("string")). - Reads(versionedObject)) // from the request + ws.Route( + addParamIf( + ws.PUT(path+"/{name}").To(h). + Doc("update the specified "+kind). + Operation("update"+kind). + Param(nameParam). + Reads(versionedObject), // from the request + namespaceParam, namespaceScope)) // TODO: Support PATCH - ws.Route(ws.DELETE(path + "/{name}").To(h). - Doc("delete the specified " + kind). - Operation("delete" + kind). - Param(ws.PathParameter("name", "name of the "+kind).DataType("string"))) + ws.Route( + addParamIf( + ws.DELETE(path+"/{name}").To(h). + Doc("delete the specified "+kind). + Operation("delete"+kind). + Param(nameParam), + namespaceParam, namespaceScope)) +} + +// Adds the given param to the given route builder if shouldAdd is true. Does nothing if shouldAdd is false. +func addParamIf(b *restful.RouteBuilder, parameter *restful.Parameter, shouldAdd bool) *restful.RouteBuilder { + if !shouldAdd { + return b + } + return b.Param(parameter) } // InstallREST registers the REST handlers (storage, watch, and operations) into a restful Container. @@ -266,8 +292,16 @@ func InstallValidator(mux Mux, servers func() map[string]Server) { func InstallSupport(container *restful.Container, ws *restful.WebService) { // TODO: convert healthz to restful and remove container arg healthz.InstallHandler(container.ServeMux) - ws.Route(ws.GET("/").To(handleIndex)) - ws.Route(ws.GET("/version").To(handleVersion)) + + // Set up a service to return the git code version. + ws.Path("/version") + ws.Doc("git code version from which this is built") + ws.Route( + ws.GET("/").To(handleVersion). + Doc("get the code version"). + Operation("getCodeVersion"). + Produces(restful.MIME_JSON). + Consumes(restful.MIME_JSON)) } // InstallLogsSupport registers the APIServer log support function into a mux. @@ -277,6 +311,22 @@ func InstallLogsSupport(mux Mux) { mux.Handle("/logs/", http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/")))) } +// Adds a service to return the supported api versions. +func AddApiWebService(container *restful.Container, apiPrefix string, versions []string) { + // TODO: InstallREST should register each version automatically + + versionHandler := APIVersionHandler(versions[:]...) + getApiVersionsWebService := new(restful.WebService) + getApiVersionsWebService.Path(apiPrefix) + getApiVersionsWebService.Doc("get available api versions") + getApiVersionsWebService.Route(getApiVersionsWebService.GET("/").To(versionHandler). + Doc("get available api versions"). + Operation("getApiVersions"). + Produces(restful.MIME_JSON). + Consumes(restful.MIME_JSON)) + container.Add(getApiVersionsWebService) +} + // handleVersion writes the server's version information. func handleVersion(req *restful.Request, resp *restful.Response) { // TODO: use restful's Response methods diff --git a/pkg/apiserver/index.go b/pkg/apiserver/index.go index 85b27b18ff5..3b05c5270f2 100644 --- a/pkg/apiserver/index.go +++ b/pkg/apiserver/index.go @@ -17,21 +17,11 @@ limitations under the License. package apiserver import ( - "fmt" + "io" "net/http" - - "github.com/emicklei/go-restful" ) // handleIndex is the root index page for Kubernetes. -func handleIndex(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Request/Response methods - if req.Request.URL.Path != "/" && req.Request.URL.Path != "/index.html" { - notFound(resp.ResponseWriter, req.Request) - return - } - resp.ResponseWriter.WriteHeader(http.StatusOK) - // TODO: serve this out of a file - data := "Welcome to Kubernetes" - fmt.Fprint(resp.ResponseWriter, data) +func HandleIndex(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, "Welcome to Kubernetes") } diff --git a/pkg/master/master.go b/pkg/master/master.go index 2329e959ce8..c36c8ffed26 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -357,19 +357,20 @@ func (m *Master) init(c *Config) { "bindings": binding.NewREST(m.bindingRegistry), } - versionHandler := apiserver.APIVersionHandler("v1beta1", "v1beta2") - + apiVersions := []string{"v1beta1", "v1beta2"} apiserver.NewAPIGroupVersion(m.API_v1beta1()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta1") apiserver.NewAPIGroupVersion(m.API_v1beta2()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta2") if c.EnableV1Beta3 { apiserver.NewAPIGroupVersion(m.API_v1beta3()).InstallREST(m.handlerContainer, c.APIPrefix, "v1beta3") - versionHandler = apiserver.APIVersionHandler("v1beta1", "v1beta2", "v1beta3") + apiVersions = []string{"v1beta1", "v1beta2", "v1beta3"} } - // TODO: InstallREST should register each version automatically - m.rootWebService.Route(m.rootWebService.GET(c.APIPrefix).To(versionHandler)) - apiserver.InstallSupport(m.handlerContainer, 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) // TODO: use go-restful apiserver.InstallValidator(m.mux, func() map[string]apiserver.Server { return m.getServersToValidate(c) }) @@ -377,7 +378,7 @@ func (m *Master) init(c *Config) { apiserver.InstallLogsSupport(m.mux) } if c.EnableUISupport { - ui.InstallSupport(m.mux) + ui.InstallSupport(m.mux, m.enableSwaggerSupport) } // TODO: install runtime/pprof handler diff --git a/pkg/ui/installsupport.go b/pkg/ui/installsupport.go index 639efd03dd3..1a96b2eb617 100644 --- a/pkg/ui/installsupport.go +++ b/pkg/ui/installsupport.go @@ -26,14 +26,16 @@ type MuxInterface interface { Handle(pattern string, handler http.Handler) } -func InstallSupport(mux MuxInterface) { +func InstallSupport(mux MuxInterface, enableSwaggerSupport bool) { // Expose files in www/ on /static/ fileServer := http.FileServer(&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "www"}) prefix := "/static/" mux.Handle(prefix, http.StripPrefix(prefix, fileServer)) - // Expose files in third_party/swagger-ui/ on /swagger-ui/ - fileServer = http.FileServer(&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "third_party/swagger-ui"}) - prefix = "/swagger-ui/" - mux.Handle(prefix, http.StripPrefix(prefix, fileServer)) + if enableSwaggerSupport { + // Expose files in third_party/swagger-ui/ on /swagger-ui + fileServer = http.FileServer(&assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: "third_party/swagger-ui"}) + prefix = "/swagger-ui/" + mux.Handle(prefix, http.StripPrefix(prefix, fileServer)) + } }