diff --git a/hack/lib/util.sh b/hack/lib/util.sh index 555958d2b3..ada6e34552 100755 --- a/hack/lib/util.sh +++ b/hack/lib/util.sh @@ -387,60 +387,6 @@ kube::util::fetch-swagger-spec() { curl -w "\n" -fs "${SWAGGER_API_PATH}logs" > "${SWAGGER_ROOT_DIR}/logs.json" } -# Takes a group/version and returns the openapi-spec file name. -# default behavior: extensions/v1beta1 -> v1beta1.extensions -# special case for v1: v1 -> v1 -kube::util::gv-to-openapi-name() { - local group_version="$1" - case "${group_version}" in - v1) - echo "v1" - ;; - *) - echo "${group_version#*/}.${group_version%/*}" - ;; - esac -} - - -# Fetches openapi spec from apiserver. -# Assumed vars: -# OPENAPI_API_PATH: Base path for openapi on apiserver. normally APIServer root. i.e., http://localhost:8080/ -# OPENAPI_ROOT_DIR: Root dir where we want to to save the fetched spec. -# VERSIONS: Array of group versions to include in swagger spec. -kube::util::fetch-openapi-spec() { - for ver in ${VERSIONS}; do - if [[ " ${KUBE_NONSERVER_GROUP_VERSIONS} " == *" ${ver} "* ]]; then - continue - fi - # fetch the openapi spec for each group version. - if [[ ${ver} == "v1" ]]; then - SUBPATH="api" - else - SUBPATH="apis" - fi - SUBPATH="${SUBPATH}/${ver}" - OPENAPI_JSON_NAME="$(kube::util::gv-to-openapi-name ${ver}).json" - curl -w "\n" -fs "${OPENAPI_PATH}${SUBPATH}/swagger.json" > "${OPENAPI_ROOT_DIR}/${OPENAPI_JSON_NAME}" - - # fetch the openapi spec for the discovery mechanism at group level. - if [[ ${ver} == "v1" ]]; then - continue - fi - SUBPATH="apis/"${ver%/*} - OPENAPI_JSON_NAME="${ver%/*}.json" - curl -w "\n" -fs "${OPENAPI_PATH}${SUBPATH}/swagger.json" > "${OPENAPI_ROOT_DIR}/${OPENAPI_JSON_NAME}" - done - - # fetch openapi specs for other discovery mechanism. - curl -w "\n" -fs "${OPENAPI_PATH}swagger.json" > "${OPENAPI_ROOT_DIR}/root_swagger.json" - curl -w "\n" -fs "${OPENAPI_PATH}version/swagger.json" > "${OPENAPI_ROOT_DIR}/version.json" - curl -w "\n" -fs "${OPENAPI_PATH}api/swagger.json" > "${OPENAPI_ROOT_DIR}/api.json" - curl -w "\n" -fs "${OPENAPI_PATH}apis/swagger.json" > "${OPENAPI_ROOT_DIR}/apis.json" - curl -w "\n" -fs "${OPENAPI_PATH}logs/swagger.json" > "${OPENAPI_ROOT_DIR}/logs.json" -} - - # Returns the name of the upstream remote repository name for the local git # repo, e.g. "upstream" or "origin". kube::util::git_upstream_remote_name() { diff --git a/hack/update-federation-openapi-spec.sh b/hack/update-federation-openapi-spec.sh index df291f5632..2faf357197 100755 --- a/hack/update-federation-openapi-spec.sh +++ b/hack/update-federation-openapi-spec.sh @@ -60,18 +60,15 @@ kube::log::status "Starting federation-apiserver" --insecure-port="${API_PORT}" \ --etcd-servers="http://${ETCD_HOST}:${ETCD_PORT}" \ --advertise-address="10.10.10.10" \ + --cert-dir="${TMP_DIR}/certs" \ --token-auth-file=$TMP_DIR/tokenauth.csv \ --service-cluster-ip-range="10.0.0.0/24" >/tmp/openapi-federation-api-server.log 2>&1 & APISERVER_PID=$! kube::util::wait_for_url "${API_HOST}:${API_PORT}/" "apiserver: " -OPENAPI_PATH="${API_HOST}:${API_PORT}/" -DEFAULT_GROUP_VERSIONS="v1 extensions/v1beta1 federation/v1beta1" -VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS} - kube::log::status "Updating " ${OPENAPI_ROOT_DIR} -OPENAPI_PATH="${OPENAPI_PATH}" OPENAPI_ROOT_DIR="${OPENAPI_ROOT_DIR}" VERSIONS="${VERSIONS}" kube::util::fetch-openapi-spec +curl -w "\n" -fs "${API_HOST}:${API_PORT}/swagger.json" > "${OPENAPI_ROOT_DIR}/swagger.json" kube::log::status "SUCCESS" diff --git a/hack/update-openapi-spec.sh b/hack/update-openapi-spec.sh index e5a4a17ee2..4195c61504 100755 --- a/hack/update-openapi-spec.sh +++ b/hack/update-openapi-spec.sh @@ -71,7 +71,7 @@ kube::util::wait_for_url "${API_HOST}:${API_PORT}/healthz" "apiserver: " kube::log::status "Updating " ${OPENAPI_ROOT_DIR} -OPENAPI_PATH="${API_HOST}:${API_PORT}/" OPENAPI_ROOT_DIR="${OPENAPI_ROOT_DIR}" VERSIONS="${KUBE_AVAILABLE_GROUP_VERSIONS}" KUBE_NONSERVER_GROUP_VERSIONS="${KUBE_NONSERVER_GROUP_VERSIONS}" kube::util::fetch-openapi-spec +curl -w "\n" -fs "${API_HOST}:${API_PORT}/swagger.json" > "${OPENAPI_ROOT_DIR}/swagger.json" kube::log::status "SUCCESS" diff --git a/pkg/apiserver/openapi/openapi.go b/pkg/apiserver/openapi/openapi.go index 7a261c5174..bbff9efb72 100644 --- a/pkg/apiserver/openapi/openapi.go +++ b/pkg/apiserver/openapi/openapi.go @@ -48,10 +48,11 @@ func ToValidOperationID(s string, capitalizeFirstLetter bool) string { return buffer.String() } -// GetOperationID returns a customize operation ID for kubernetes API server's OpenAPI spec to prevent duplicate IDs. -func GetOperationID(servePath string, r *restful.Route) (string, error) { +// GetOperationIDAndTags returns a customize operation ID and a list of tags for kubernetes API server's OpenAPI spec to prevent duplicate IDs. +func GetOperationIDAndTags(servePath string, r *restful.Route) (string, []string, error) { op := r.Operation path := r.Path + var tags []string // TODO: This is hacky, figure out where this name conflict is created and fix it at the root. if strings.HasPrefix(path, "/apis/extensions/v1beta1/namespaces/{namespace}/") && strings.HasSuffix(op, "ScaleScale") { op = op[:len(op)-10] + strings.Title(strings.Split(path[48:], "/")[0]) + "Scale" @@ -60,7 +61,7 @@ func GetOperationID(servePath string, r *restful.Route) (string, error) { case "/swagger.json": prefix, exists := verbs.GetPrefix(op) if !exists { - return op, fmt.Errorf("operation names should start with a verb. Cannot determine operation verb from %v", op) + return op, tags, fmt.Errorf("operation names should start with a verb. Cannot determine operation verb from %v", op) } op = op[len(prefix):] parts := strings.Split(strings.Trim(path, "/"), "/") @@ -70,12 +71,17 @@ func GetOperationID(servePath string, r *restful.Route) (string, error) { } if len(parts) >= 2 && parts[0] == "apis" { prefix = prefix + ToValidOperationID(strings.TrimSuffix(parts[1], ".k8s.io"), prefix != "") + tag := ToValidOperationID(strings.TrimSuffix(parts[1], ".k8s.io"), false) if len(parts) > 2 { prefix = prefix + ToValidOperationID(parts[2], prefix != "") + tag = tag + "_" + ToValidOperationID(parts[2], false) } + tags = append(tags, tag) + } else if len(parts) >= 1 { + tags = append(tags, ToValidOperationID(parts[0], false)) } - return prefix + ToValidOperationID(op, prefix != ""), nil + return prefix + ToValidOperationID(op, prefix != ""), tags, nil default: - return op, nil + return op, tags, nil } } diff --git a/pkg/genericapiserver/config.go b/pkg/genericapiserver/config.go index b2aa6e08b9..13eb00fad8 100644 --- a/pkg/genericapiserver/config.go +++ b/pkg/genericapiserver/config.go @@ -223,7 +223,7 @@ func NewConfig() *Config { Description: "Default Response.", }, }, - GetOperationID: apiserveropenapi.GetOperationID, + GetOperationIDAndTags: apiserveropenapi.GetOperationIDAndTags, }, LongRunningFunc: genericfilters.BasicLongRunningRequestCheck(longRunningRE, map[string]string{"watch": "true"}), } diff --git a/pkg/genericapiserver/openapi/common/common.go b/pkg/genericapiserver/openapi/common/common.go index b639ecbd30..d2a80f2e90 100644 --- a/pkg/genericapiserver/openapi/common/common.go +++ b/pkg/genericapiserver/openapi/common/common.go @@ -61,8 +61,8 @@ type Config struct { // or any of the models will result in spec generation failure. Definitions *OpenAPIDefinitions - // GetOperationID returns operation id for a restful route. It is an optional function to customize operation IDs. - GetOperationID func(servePath string, r *restful.Route) (string, error) + // GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs. + GetOperationIDAndTags func(servePath string, r *restful.Route) (string, []string, error) // SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config // is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses. diff --git a/pkg/genericapiserver/openapi/openapi.go b/pkg/genericapiserver/openapi/openapi.go index 59f4b14419..8f540be266 100644 --- a/pkg/genericapiserver/openapi/openapi.go +++ b/pkg/genericapiserver/openapi/openapi.go @@ -74,9 +74,9 @@ func RegisterOpenAPIService(servePath string, webServices []*restful.WebService, } func (o *openAPI) init(webServices []*restful.WebService) error { - if o.config.GetOperationID == nil { - o.config.GetOperationID = func(_ string, r *restful.Route) (string, error) { - return r.Operation, nil + if o.config.GetOperationIDAndTags == nil { + o.config.GetOperationIDAndTags = func(_ string, r *restful.Route) (string, []string, error) { + return r.Operation, nil, nil } } if o.config.CommonResponses == nil { @@ -217,7 +217,7 @@ func (o *openAPI) buildOperations(route restful.Route, inPathCommonParamsMap map }, }, } - if ret.ID, err = o.config.GetOperationID(o.servePath, &route); err != nil { + if ret.ID, ret.Tags, err = o.config.GetOperationIDAndTags(o.servePath, &route); err != nil { return ret, err } diff --git a/pkg/genericapiserver/routes/BUILD b/pkg/genericapiserver/routes/BUILD index 8f6dff3d1e..e1e3de4d77 100644 --- a/pkg/genericapiserver/routes/BUILD +++ b/pkg/genericapiserver/routes/BUILD @@ -33,7 +33,6 @@ go_library( "//vendor:github.com/elazarl/go-bindata-assetfs", "//vendor:github.com/emicklei/go-restful", "//vendor:github.com/emicklei/go-restful/swagger", - "//vendor:github.com/go-openapi/spec", "//vendor:github.com/golang/glog", ], ) diff --git a/pkg/genericapiserver/routes/openapi.go b/pkg/genericapiserver/routes/openapi.go index 6ed3e78256..78b7675865 100644 --- a/pkg/genericapiserver/routes/openapi.go +++ b/pkg/genericapiserver/routes/openapi.go @@ -17,14 +17,10 @@ limitations under the License. package routes import ( - "strings" - "k8s.io/kubernetes/pkg/genericapiserver/mux" "k8s.io/kubernetes/pkg/genericapiserver/openapi" "k8s.io/kubernetes/pkg/genericapiserver/openapi/common" - "github.com/emicklei/go-restful" - "github.com/go-openapi/spec" "github.com/golang/glog" ) @@ -35,22 +31,6 @@ type OpenAPI struct { // Install adds the SwaggerUI webservice to the given mux. func (oa OpenAPI) Install(c *mux.APIContainer) { - // Install one spec per web service, an ideal client will have a ClientSet containing one client - // per each of these specs. - for _, w := range c.RegisteredWebServices() { - if strings.HasPrefix(w.RootPath(), "/swaggerapi") { - continue - } - - config := *oa.Config - config.Info = new(spec.Info) - *config.Info = *oa.Config.Info - config.Info.Title = config.Info.Title + " " + w.RootPath() - err := openapi.RegisterOpenAPIService(w.RootPath()+"/swagger.json", []*restful.WebService{w}, &config, c) - if err != nil { - glog.Fatalf("Failed to register open api spec for %v: %v", w.RootPath(), err) - } - } err := openapi.RegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, c) if err != nil { glog.Fatalf("Failed to register open api spec for root: %v", err) diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index e0405b6973..a1e26743e3 100644 --- a/pkg/master/master_test.go +++ b/pkg/master/master_test.go @@ -24,7 +24,6 @@ import ( "net/http" "net/http/httptest" "reflect" - "strings" "testing" "k8s.io/kubernetes/pkg/api" @@ -519,10 +518,6 @@ func TestValidOpenAPISpec(t *testing.T) { // TODO(mehdy): The actual validation part of these tests are timing out on jerkin but passing locally. Enable it after debugging timeout issue. disableValidation := true - // Saving specs to a temporary folder is a good way to debug spec generation without bringing up an actual - // api server. - saveSwaggerSpecs := false - // Validate OpenApi spec doc, err := loads.Spec(server.URL + "/swagger.json") if assert.NoError(err) { @@ -537,46 +532,4 @@ func TestValidOpenAPISpec(t *testing.T) { t.Logf("Validation is disabled because it is timing out on jenkins put passing locally.") } } - - // validate specs on each end-point - resp, err = http.Get(server.URL) - if !assert.NoError(err) { - t.Errorf("unexpected error: %v", err) - } - assert.Equal(http.StatusOK, resp.StatusCode) - var list unversioned.RootPaths - if assert.NoError(decodeResponse(resp, &list)) { - for _, path := range list.Paths { - if !strings.HasPrefix(path, "/api") { - continue - } - t.Logf("Validating open API spec on %v ...", path) - - if saveSwaggerSpecs { - resp, err = http.Get(server.URL + path + "/swagger.json") - if !assert.NoError(err) { - t.Errorf("unexpected error: %v", err) - } - assert.Equal(http.StatusOK, resp.StatusCode) - assert.NoError(writeResponseToFile(resp, "/tmp/swagger_"+strings.Replace(path, "/", "_", -1)+".json")) - } - - // Validate OpenApi spec on path - doc, err := loads.Spec(server.URL + path + "/swagger.json") - if assert.NoError(err) { - validator := validate.NewSpecValidator(doc.Schema(), strfmt.Default) - if !disableValidation { - res, warns := validator.Validate(doc) - assert.NoError(res.AsError()) - if !warns.IsValid() { - t.Logf("Open API spec on %v has some warnings : %v", path, warns) - } - } else { - t.Logf("Validation is disabled because it is timing out on jenkins but passing locally.") - } - } - - } - } - }