- Add GroupVersion as tags to OpenAPI spec

- Remove all end-point specs as they are not useful in light of GroupVersion tags in main spec
pull/6/head
mbohlool 2016-10-22 23:49:59 -07:00
parent 8ff18565b8
commit 75451b49f4
10 changed files with 21 additions and 140 deletions

View File

@ -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() {

View File

@ -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"

View File

@ -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"

View File

@ -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
}
}

View File

@ -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"}),
}

View File

@ -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.

View File

@ -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
}

View File

@ -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",
],
)

View File

@ -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)

View File

@ -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.")
}
}
}
}
}