Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to improve swagger ui #3298

Merged
merged 1 commit into from
Jan 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 76 additions & 26 deletions pkg/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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"
Expand All @@ -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(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.GET(path+"/{name}").To(h).
Doc("read the specified "+kind).
Operation("read"+kind).
Param(nameParam).
Writes(versionedObject), // on the response
namespaceParam, namespaceScope))

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.
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be a little nicer to put AddApiWebService and APIVersionHandler (which could be changed to apiVersionHandler) next to each other, and handleVersion and InstallSupport (which could be renamed InstallVersion) next to each other, but that can be left to a future PR.

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
Expand Down
16 changes: 3 additions & 13 deletions pkg/apiserver/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 := "<html><body>Welcome to Kubernetes</body></html>"
fmt.Fprint(resp.ResponseWriter, data)
func HandleIndex(w http.ResponseWriter, r *http.Request) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than removing the go-restful args?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Mainly removing the go-restful args.
I didnt find the code for checking the path useful. None of the handlers do that check.
I can add it back if you think its useful.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that's fine. Maybe this was a default handler at some point.

io.WriteString(w, "<html><body>Welcome to Kubernetes</body></html>")
}
15 changes: 8 additions & 7 deletions pkg/master/master.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,27 +357,28 @@ 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) })
if c.EnableLogsSupport {
apiserver.InstallLogsSupport(m.mux)
}
if c.EnableUISupport {
ui.InstallSupport(m.mux)
ui.InstallSupport(m.mux, m.enableSwaggerSupport)
}

// TODO: install runtime/pprof handler
Expand Down
12 changes: 7 additions & 5 deletions pkg/ui/installsupport.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 <host>/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 <host>/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 <host>/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))
}
}