From e671fe20d7a664eeea98e15b4c330c1be72476cd Mon Sep 17 00:00:00 2001 From: mbohlool Date: Wed, 19 Jul 2017 23:34:37 -0700 Subject: [PATCH] Update main repo references to new kube-openapi repo Kubernetes-commit: 400b77b48f972b1e10854980586559d5852088c7 --- pkg/endpoints/openapi/openapi.go | 4 +- pkg/server/config.go | 2 +- pkg/server/genericapiserver.go | 2 +- pkg/server/genericapiserver_test.go | 2 +- pkg/server/openapi/OWNERS | 7 - pkg/server/openapi/doc.go | 20 -- pkg/server/openapi/openapi.go | 519 ---------------------------- pkg/server/openapi/openapi_test.go | 465 ------------------------- pkg/server/openapi/util.go | 61 ---- pkg/server/routes/openapi.go | 13 +- pkg/util/trie/BUILD | 14 - pkg/util/trie/trie.go | 79 ----- 12 files changed, 11 insertions(+), 1177 deletions(-) delete mode 100755 pkg/server/openapi/OWNERS delete mode 100644 pkg/server/openapi/doc.go delete mode 100644 pkg/server/openapi/openapi.go delete mode 100644 pkg/server/openapi/openapi_test.go delete mode 100644 pkg/server/openapi/util.go delete mode 100644 pkg/util/trie/BUILD delete mode 100644 pkg/util/trie/trie.go diff --git a/pkg/endpoints/openapi/openapi.go b/pkg/endpoints/openapi/openapi.go index 352ebcb2f..7b8f3589e 100644 --- a/pkg/endpoints/openapi/openapi.go +++ b/pkg/endpoints/openapi/openapi.go @@ -30,10 +30,10 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apiserver/pkg/util/trie" + "k8s.io/kube-openapi/pkg/util" ) -var verbs = trie.New([]string{"get", "log", "read", "replace", "patch", "delete", "deletecollection", "watch", "connect", "proxy", "list", "create", "patch"}) +var verbs = util.NewTrie([]string{"get", "log", "read", "replace", "patch", "delete", "deletecollection", "watch", "connect", "proxy", "list", "create", "patch"}) const ( extensionGVK = "x-kubernetes-group-version-kind" diff --git a/pkg/server/config.go b/pkg/server/config.go index a0bf338f5..e97313d92 100644 --- a/pkg/server/config.go +++ b/pkg/server/config.go @@ -33,7 +33,6 @@ import ( "github.com/go-openapi/spec" "github.com/pborman/uuid" - openapicommon "k8s.io/apimachinery/pkg/openapi" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/util/sets" @@ -61,6 +60,7 @@ import ( "k8s.io/client-go/informers" restclient "k8s.io/client-go/rest" certutil "k8s.io/client-go/util/cert" + openapicommon "k8s.io/kube-openapi/pkg/common" _ "k8s.io/apiserver/pkg/apis/apiserver/install" ) diff --git a/pkg/server/genericapiserver.go b/pkg/server/genericapiserver.go index c9df18059..266f53ec2 100644 --- a/pkg/server/genericapiserver.go +++ b/pkg/server/genericapiserver.go @@ -30,7 +30,6 @@ import ( "k8s.io/apimachinery/pkg/apimachinery" "k8s.io/apimachinery/pkg/apimachinery/registered" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - openapicommon "k8s.io/apimachinery/pkg/openapi" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -44,6 +43,7 @@ import ( "k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/routes" restclient "k8s.io/client-go/rest" + openapicommon "k8s.io/kube-openapi/pkg/common" ) // Info about an API group. diff --git a/pkg/server/genericapiserver_test.go b/pkg/server/genericapiserver_test.go index b8aed64c1..a69e0240b 100644 --- a/pkg/server/genericapiserver_test.go +++ b/pkg/server/genericapiserver_test.go @@ -34,7 +34,6 @@ import ( "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/apimachinery" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/openapi" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -51,6 +50,7 @@ import ( "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" restclient "k8s.io/client-go/rest" + openapi "k8s.io/kube-openapi/pkg/common" ) const ( diff --git a/pkg/server/openapi/OWNERS b/pkg/server/openapi/OWNERS deleted file mode 100755 index 7c8ad60c5..000000000 --- a/pkg/server/openapi/OWNERS +++ /dev/null @@ -1,7 +0,0 @@ -reviewers: -- yujuhong -- gmarek -- mbohlool -- philips -approvers: -- mbohlool diff --git a/pkg/server/openapi/doc.go b/pkg/server/openapi/doc.go deleted file mode 100644 index 091580bc4..000000000 --- a/pkg/server/openapi/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -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 openapi contains code to generate OpenAPI discovery spec (which -// initial version of it also known as Swagger 2.0). -// For more details: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md -package openapi diff --git a/pkg/server/openapi/openapi.go b/pkg/server/openapi/openapi.go deleted file mode 100644 index f0fcc1311..000000000 --- a/pkg/server/openapi/openapi.go +++ /dev/null @@ -1,519 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -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 openapi - -import ( - "bytes" - "compress/gzip" - "crypto/sha512" - "encoding/json" - "fmt" - "gopkg.in/yaml.v2" - "mime" - "net/http" - "reflect" - "strings" - "time" - - restful "github.com/emicklei/go-restful" - "github.com/go-openapi/spec" - "github.com/golang/protobuf/proto" - "github.com/googleapis/gnostic/OpenAPIv2" - "github.com/googleapis/gnostic/compiler" - - "k8s.io/apimachinery/pkg/openapi" - genericmux "k8s.io/apiserver/pkg/server/mux" - "k8s.io/apiserver/pkg/util/trie" -) - -const ( - OpenAPIVersion = "2.0" - extensionPrefix = "x-kubernetes-" - - JSON_EXT = ".json" - - MIME_JSON = "application/json" - // TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags. - MIME_PB = "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf" - MIME_PB_GZ = "application/x-gzip" -) - -type openAPI struct { - config *openapi.Config - swagger *spec.Swagger - swaggerBytes []byte - swaggerPb []byte - swaggerPbGz []byte - lastModified time.Time - protocolList []string - definitions map[string]openapi.OpenAPIDefinition -} - -func computeEtag(data []byte) string { - return fmt.Sprintf("\"%X\"", sha512.Sum512(data)) -} - -// RegisterOpenAPIService registers a handler to provides standard OpenAPI specification. -func RegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *openapi.Config, mux *genericmux.PathRecorderMux) (err error) { - - if !strings.HasSuffix(servePath, JSON_EXT) { - return fmt.Errorf("Serving path must ends with \"%s\".", JSON_EXT) - } - - servePathBase := servePath[:len(servePath)-len(JSON_EXT)] - - o := openAPI{ - config: config, - swagger: &spec.Swagger{ - SwaggerProps: spec.SwaggerProps{ - Swagger: OpenAPIVersion, - Definitions: spec.Definitions{}, - Paths: &spec.Paths{Paths: map[string]spec.PathItem{}}, - Info: config.Info, - }, - }, - } - - err = o.init(webServices) - if err != nil { - return err - } - - mime.AddExtensionType(".json", MIME_JSON) - mime.AddExtensionType(".pb-v1", MIME_PB) - mime.AddExtensionType(".gz", MIME_PB_GZ) - - type fileInfo struct { - ext string - data []byte - } - - files := []fileInfo{ - {".json", o.swaggerBytes}, - {"-2.0.0.json", o.swaggerBytes}, - {"-2.0.0.pb-v1", o.swaggerPb}, - {"-2.0.0.pb-v1.gz", o.swaggerPbGz}, - } - - for _, file := range files { - path := servePathBase + file.ext - data := file.data - etag := computeEtag(file.data) - mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != path { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte("Path not found!")) - return - } - w.Header().Set("Etag", etag) - // ServeContent will take care of caching using eTag. - http.ServeContent(w, r, path, o.lastModified, bytes.NewReader(data)) - }) - } - - return nil -} - -func (o *openAPI) init(webServices []*restful.WebService) error { - if o.config.GetOperationIDAndTags == nil { - o.config.GetOperationIDAndTags = func(r *restful.Route) (string, []string, error) { - return r.Operation, nil, nil - } - } - if o.config.GetDefinitionName == nil { - o.config.GetDefinitionName = func(name string) (string, spec.Extensions) { - return name[strings.LastIndex(name, "/")+1:], nil - } - } - o.definitions = o.config.GetDefinitions(func(name string) spec.Ref { - defName, _ := o.config.GetDefinitionName(name) - return spec.MustCreateRef("#/definitions/" + openapi.EscapeJsonPointer(defName)) - }) - if o.config.CommonResponses == nil { - o.config.CommonResponses = map[int]spec.Response{} - } - err := o.buildPaths(webServices) - if err != nil { - return err - } - if o.config.SecurityDefinitions != nil { - o.swagger.SecurityDefinitions = *o.config.SecurityDefinitions - o.swagger.Security = o.config.DefaultSecurity - } - if o.config.PostProcessSpec != nil { - o.swagger, err = o.config.PostProcessSpec(o.swagger) - if err != nil { - return err - } - } - - o.swaggerBytes, err = json.MarshalIndent(o.swagger, " ", " ") - if err != nil { - return err - } - o.swaggerPb, err = toProtoBinary(o.swaggerBytes) - if err != nil { - return err - } - o.swaggerPbGz = toGzip(o.swaggerPb) - o.lastModified = time.Now() - - return nil -} - -func toProtoBinary(spec []byte) ([]byte, error) { - var info yaml.MapSlice - err := yaml.Unmarshal(spec, &info) - if err != nil { - return nil, err - } - document, err := openapi_v2.NewDocument(info, compiler.NewContext("$root", nil)) - if err != nil { - return nil, err - } - return proto.Marshal(document) -} - -func toGzip(data []byte) []byte { - var buf bytes.Buffer - zw := gzip.NewWriter(&buf) - zw.Write(data) - zw.Close() - return buf.Bytes() -} - -func getCanonicalizeTypeName(t reflect.Type) string { - if t.PkgPath() == "" { - return t.Name() - } - path := t.PkgPath() - if strings.Contains(path, "/vendor/") { - path = path[strings.Index(path, "/vendor/")+len("/vendor/"):] - } - return path + "." + t.Name() -} - -func (o *openAPI) buildDefinitionRecursively(name string) error { - uniqueName, extensions := o.config.GetDefinitionName(name) - if _, ok := o.swagger.Definitions[uniqueName]; ok { - return nil - } - if item, ok := o.definitions[name]; ok { - schema := spec.Schema{ - VendorExtensible: item.Schema.VendorExtensible, - SchemaProps: item.Schema.SchemaProps, - SwaggerSchemaProps: item.Schema.SwaggerSchemaProps, - } - if extensions != nil { - if schema.Extensions == nil { - schema.Extensions = spec.Extensions{} - } - for k, v := range extensions { - schema.Extensions[k] = v - } - } - o.swagger.Definitions[uniqueName] = schema - for _, v := range item.Dependencies { - if err := o.buildDefinitionRecursively(v); err != nil { - return err - } - } - } else { - return fmt.Errorf("cannot find model definition for %v. If you added a new type, you may need to add +k8s:openapi-gen=true to the package or type and run code-gen again.", name) - } - return nil -} - -// buildDefinitionForType build a definition for a given type and return a referable name to it's definition. -// This is the main function that keep track of definitions used in this spec and is depend on code generated -// by k8s.io/kube-gen/cmd/openapi-gen. -func (o *openAPI) buildDefinitionForType(sample interface{}) (string, error) { - t := reflect.TypeOf(sample) - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - name := getCanonicalizeTypeName(t) - if err := o.buildDefinitionRecursively(name); err != nil { - return "", err - } - defName, _ := o.config.GetDefinitionName(name) - return "#/definitions/" + openapi.EscapeJsonPointer(defName), nil -} - -// buildPaths builds OpenAPI paths using go-restful's web services. -func (o *openAPI) buildPaths(webServices []*restful.WebService) error { - pathsToIgnore := trie.New(o.config.IgnorePrefixes) - duplicateOpId := make(map[string]string) - for _, w := range webServices { - rootPath := w.RootPath() - if pathsToIgnore.HasPrefix(rootPath) { - continue - } - commonParams, err := o.buildParameters(w.PathParameters()) - if err != nil { - return err - } - for path, routes := range groupRoutesByPath(w.Routes()) { - // go-swagger has special variable definition {$NAME:*} that can only be - // used at the end of the path and it is not recognized by OpenAPI. - if strings.HasSuffix(path, ":*}") { - path = path[:len(path)-3] + "}" - } - if pathsToIgnore.HasPrefix(path) { - continue - } - // Aggregating common parameters make API spec (and generated clients) simpler - inPathCommonParamsMap, err := o.findCommonParameters(routes) - if err != nil { - return err - } - pathItem, exists := o.swagger.Paths.Paths[path] - if exists { - return fmt.Errorf("duplicate webservice route has been found for path: %v", path) - } - pathItem = spec.PathItem{ - PathItemProps: spec.PathItemProps{ - Parameters: make([]spec.Parameter, 0), - }, - } - // add web services's parameters as well as any parameters appears in all ops, as common parameters - pathItem.Parameters = append(pathItem.Parameters, commonParams...) - for _, p := range inPathCommonParamsMap { - pathItem.Parameters = append(pathItem.Parameters, p) - } - sortParameters(pathItem.Parameters) - for _, route := range routes { - op, err := o.buildOperations(route, inPathCommonParamsMap) - sortParameters(op.Parameters) - if err != nil { - return err - } - dpath, exists := duplicateOpId[op.ID] - if exists { - return fmt.Errorf("Duplicate Operation ID %v for path %v and %v.", op.ID, dpath, path) - } else { - duplicateOpId[op.ID] = path - } - switch strings.ToUpper(route.Method) { - case "GET": - pathItem.Get = op - case "POST": - pathItem.Post = op - case "HEAD": - pathItem.Head = op - case "PUT": - pathItem.Put = op - case "DELETE": - pathItem.Delete = op - case "OPTIONS": - pathItem.Options = op - case "PATCH": - pathItem.Patch = op - } - } - o.swagger.Paths.Paths[path] = pathItem - } - } - return nil -} - -// buildOperations builds operations for each webservice path -func (o *openAPI) buildOperations(route restful.Route, inPathCommonParamsMap map[interface{}]spec.Parameter) (ret *spec.Operation, err error) { - ret = &spec.Operation{ - OperationProps: spec.OperationProps{ - Description: route.Doc, - Consumes: route.Consumes, - Produces: route.Produces, - Schemes: o.config.ProtocolList, - Responses: &spec.Responses{ - ResponsesProps: spec.ResponsesProps{ - StatusCodeResponses: make(map[int]spec.Response), - }, - }, - }, - } - for k, v := range route.Metadata { - if strings.HasPrefix(k, extensionPrefix) { - if ret.Extensions == nil { - ret.Extensions = spec.Extensions{} - } - ret.Extensions.Add(k, v) - } - } - if ret.ID, ret.Tags, err = o.config.GetOperationIDAndTags(&route); err != nil { - return ret, err - } - - // Build responses - for _, resp := range route.ResponseErrors { - ret.Responses.StatusCodeResponses[resp.Code], err = o.buildResponse(resp.Model, resp.Message) - if err != nil { - return ret, err - } - } - // If there is no response but a write sample, assume that write sample is an http.StatusOK response. - if len(ret.Responses.StatusCodeResponses) == 0 && route.WriteSample != nil { - ret.Responses.StatusCodeResponses[http.StatusOK], err = o.buildResponse(route.WriteSample, "OK") - if err != nil { - return ret, err - } - } - for code, resp := range o.config.CommonResponses { - if _, exists := ret.Responses.StatusCodeResponses[code]; !exists { - ret.Responses.StatusCodeResponses[code] = resp - } - } - // If there is still no response, use default response provided. - if len(ret.Responses.StatusCodeResponses) == 0 { - ret.Responses.Default = o.config.DefaultResponse - } - - // Build non-common Parameters - ret.Parameters = make([]spec.Parameter, 0) - for _, param := range route.ParameterDocs { - if _, isCommon := inPathCommonParamsMap[mapKeyFromParam(param)]; !isCommon { - openAPIParam, err := o.buildParameter(param.Data(), route.ReadSample) - if err != nil { - return ret, err - } - ret.Parameters = append(ret.Parameters, openAPIParam) - } - } - return ret, nil -} - -func (o *openAPI) buildResponse(model interface{}, description string) (spec.Response, error) { - schema, err := o.toSchema(model) - if err != nil { - return spec.Response{}, err - } - return spec.Response{ - ResponseProps: spec.ResponseProps{ - Description: description, - Schema: schema, - }, - }, nil -} - -func (o *openAPI) findCommonParameters(routes []restful.Route) (map[interface{}]spec.Parameter, error) { - commonParamsMap := make(map[interface{}]spec.Parameter, 0) - paramOpsCountByName := make(map[interface{}]int, 0) - paramNameKindToDataMap := make(map[interface{}]restful.ParameterData, 0) - for _, route := range routes { - routeParamDuplicateMap := make(map[interface{}]bool) - s := "" - for _, param := range route.ParameterDocs { - m, _ := json.Marshal(param.Data()) - s += string(m) + "\n" - key := mapKeyFromParam(param) - if routeParamDuplicateMap[key] { - msg, _ := json.Marshal(route.ParameterDocs) - return commonParamsMap, fmt.Errorf("duplicate parameter %v for route %v, %v.", param.Data().Name, string(msg), s) - } - routeParamDuplicateMap[key] = true - paramOpsCountByName[key]++ - paramNameKindToDataMap[key] = param.Data() - } - } - for key, count := range paramOpsCountByName { - paramData := paramNameKindToDataMap[key] - if count == len(routes) && paramData.Kind != restful.BodyParameterKind { - openAPIParam, err := o.buildParameter(paramData, nil) - if err != nil { - return commonParamsMap, err - } - commonParamsMap[key] = openAPIParam - } - } - return commonParamsMap, nil -} - -func (o *openAPI) toSchema(model interface{}) (_ *spec.Schema, err error) { - if openAPIType, openAPIFormat := openapi.GetOpenAPITypeFormat(getCanonicalizeTypeName(reflect.TypeOf(model))); openAPIType != "" { - return &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{openAPIType}, - Format: openAPIFormat, - }, - }, nil - } else { - ref, err := o.buildDefinitionForType(model) - if err != nil { - return nil, err - } - return &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: spec.MustCreateRef(ref), - }, - }, nil - } -} - -func (o *openAPI) buildParameter(restParam restful.ParameterData, bodySample interface{}) (ret spec.Parameter, err error) { - ret = spec.Parameter{ - ParamProps: spec.ParamProps{ - Name: restParam.Name, - Description: restParam.Description, - Required: restParam.Required, - }, - } - switch restParam.Kind { - case restful.BodyParameterKind: - if bodySample != nil { - ret.In = "body" - ret.Schema, err = o.toSchema(bodySample) - return ret, err - } else { - // There is not enough information in the body parameter to build the definition. - // Body parameter has a data type that is a short name but we need full package name - // of the type to create a definition. - return ret, fmt.Errorf("restful body parameters are not supported: %v", restParam.DataType) - } - case restful.PathParameterKind: - ret.In = "path" - if !restParam.Required { - return ret, fmt.Errorf("path parameters should be marked at required for parameter %v", restParam) - } - case restful.QueryParameterKind: - ret.In = "query" - case restful.HeaderParameterKind: - ret.In = "header" - case restful.FormParameterKind: - ret.In = "formData" - default: - return ret, fmt.Errorf("unknown restful operation kind : %v", restParam.Kind) - } - openAPIType, openAPIFormat := openapi.GetOpenAPITypeFormat(restParam.DataType) - if openAPIType == "" { - return ret, fmt.Errorf("non-body Restful parameter type should be a simple type, but got : %v", restParam.DataType) - } - ret.Type = openAPIType - ret.Format = openAPIFormat - ret.UniqueItems = !restParam.AllowMultiple - return ret, nil -} - -func (o *openAPI) buildParameters(restParam []*restful.Parameter) (ret []spec.Parameter, err error) { - ret = make([]spec.Parameter, len(restParam)) - for i, v := range restParam { - ret[i], err = o.buildParameter(v.Data(), nil) - if err != nil { - return ret, err - } - } - return ret, nil -} diff --git a/pkg/server/openapi/openapi_test.go b/pkg/server/openapi/openapi_test.go deleted file mode 100644 index 64d519553..000000000 --- a/pkg/server/openapi/openapi_test.go +++ /dev/null @@ -1,465 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -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 openapi - -import ( - "encoding/json" - "fmt" - "net/http" - "strings" - "testing" - - "github.com/emicklei/go-restful" - "github.com/go-openapi/spec" - "github.com/stretchr/testify/assert" - "k8s.io/apimachinery/pkg/openapi" -) - -// setUp is a convenience function for setting up for (most) tests. -func setUp(t *testing.T, fullMethods bool) (openAPI, *restful.Container, *assert.Assertions) { - assert := assert.New(t) - config, container := getConfig(fullMethods) - return openAPI{ - config: config, - swagger: &spec.Swagger{ - SwaggerProps: spec.SwaggerProps{ - Swagger: OpenAPIVersion, - Definitions: spec.Definitions{}, - Paths: &spec.Paths{Paths: map[string]spec.PathItem{}}, - Info: config.Info, - }, - }, - }, container, assert -} - -func noOp(request *restful.Request, response *restful.Response) {} - -// Test input -type TestInput struct { - // Name of the input - Name string `json:"name,omitempty"` - // ID of the input - ID int `json:"id,omitempty"` - Tags []string `json:"tags,omitempty"` -} - -// Test output -type TestOutput struct { - // Name of the output - Name string `json:"name,omitempty"` - // Number of outputs - Count int `json:"count,omitempty"` -} - -func (_ TestInput) OpenAPIDefinition() *openapi.OpenAPIDefinition { - schema := spec.Schema{} - schema.Description = "Test input" - schema.Properties = map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the input", - Type: []string{"string"}, - Format: "", - }, - }, - "id": { - SchemaProps: spec.SchemaProps{ - Description: "ID of the input", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "tags": { - SchemaProps: spec.SchemaProps{ - Description: "", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } - schema.Extensions = spec.Extensions{"x-test": "test"} - return &openapi.OpenAPIDefinition{ - Schema: schema, - Dependencies: []string{}, - } -} - -func (_ TestOutput) OpenAPIDefinition() *openapi.OpenAPIDefinition { - schema := spec.Schema{} - schema.Description = "Test output" - schema.Properties = map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the output", - Type: []string{"string"}, - Format: "", - }, - }, - "count": { - SchemaProps: spec.SchemaProps{ - Description: "Number of outputs", - Type: []string{"integer"}, - Format: "int32", - }, - }, - } - return &openapi.OpenAPIDefinition{ - Schema: schema, - Dependencies: []string{}, - } -} - -var _ openapi.OpenAPIDefinitionGetter = TestInput{} -var _ openapi.OpenAPIDefinitionGetter = TestOutput{} - -func getTestRoute(ws *restful.WebService, method string, additionalParams bool, opPrefix string) *restful.RouteBuilder { - ret := ws.Method(method). - Path("/test/{path:*}"). - Doc(fmt.Sprintf("%s test input", method)). - Operation(fmt.Sprintf("%s%sTestInput", method, opPrefix)). - Produces(restful.MIME_JSON). - Consumes(restful.MIME_JSON). - Param(ws.PathParameter("path", "path to the resource").DataType("string")). - Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). - Reads(TestInput{}). - Returns(200, "OK", TestOutput{}). - Writes(TestOutput{}). - To(noOp) - if additionalParams { - ret.Param(ws.HeaderParameter("hparam", "a test head parameter").DataType("integer")) - ret.Param(ws.FormParameter("fparam", "a test form parameter").DataType("number")) - } - return ret -} - -func getConfig(fullMethods bool) (*openapi.Config, *restful.Container) { - mux := http.NewServeMux() - container := restful.NewContainer() - container.ServeMux = mux - ws := new(restful.WebService) - ws.Path("/foo") - ws.Route(getTestRoute(ws, "get", true, "foo")) - if fullMethods { - ws.Route(getTestRoute(ws, "post", false, "foo")). - Route(getTestRoute(ws, "put", false, "foo")). - Route(getTestRoute(ws, "head", false, "foo")). - Route(getTestRoute(ws, "patch", false, "foo")). - Route(getTestRoute(ws, "options", false, "foo")). - Route(getTestRoute(ws, "delete", false, "foo")) - - } - ws.Path("/bar") - ws.Route(getTestRoute(ws, "get", true, "bar")) - if fullMethods { - ws.Route(getTestRoute(ws, "post", false, "bar")). - Route(getTestRoute(ws, "put", false, "bar")). - Route(getTestRoute(ws, "head", false, "bar")). - Route(getTestRoute(ws, "patch", false, "bar")). - Route(getTestRoute(ws, "options", false, "bar")). - Route(getTestRoute(ws, "delete", false, "bar")) - - } - container.Add(ws) - return &openapi.Config{ - ProtocolList: []string{"https"}, - Info: &spec.Info{ - InfoProps: spec.InfoProps{ - Title: "TestAPI", - Description: "Test API", - Version: "unversioned", - }, - }, - GetDefinitions: func(_ openapi.ReferenceCallback) map[string]openapi.OpenAPIDefinition { - return map[string]openapi.OpenAPIDefinition{ - "k8s.io/apiserver/pkg/server/openapi.TestInput": *TestInput{}.OpenAPIDefinition(), - "k8s.io/apiserver/pkg/server/openapi.TestOutput": *TestOutput{}.OpenAPIDefinition(), - // Bazel changes the package name, this is ok for testing, but we need to fix it if it happened - // in the main code. - "k8s.io/apiserver/pkg/server/openapi/go_default_test.TestInput": *TestInput{}.OpenAPIDefinition(), - "k8s.io/apiserver/pkg/server/openapi/go_default_test.TestOutput": *TestOutput{}.OpenAPIDefinition(), - } - }, - GetDefinitionName: func(name string) (string, spec.Extensions) { - friendlyName := name[strings.LastIndex(name, "/")+1:] - if strings.HasPrefix(friendlyName, "go_default_test") { - friendlyName = "openapi" + friendlyName[len("go_default_test"):] - } - return friendlyName, spec.Extensions{"x-test2": "test2"} - }, - }, container -} - -func getTestOperation(method string, opPrefix string) *spec.Operation { - return &spec.Operation{ - OperationProps: spec.OperationProps{ - Description: fmt.Sprintf("%s test input", method), - Consumes: []string{"application/json"}, - Produces: []string{"application/json"}, - Schemes: []string{"https"}, - Parameters: []spec.Parameter{}, - Responses: getTestResponses(), - ID: fmt.Sprintf("%s%sTestInput", method, opPrefix), - }, - } -} - -func getTestPathItem(allMethods bool, opPrefix string) spec.PathItem { - ret := spec.PathItem{ - PathItemProps: spec.PathItemProps{ - Get: getTestOperation("get", opPrefix), - Parameters: getTestCommonParameters(), - }, - } - ret.Get.Parameters = getAdditionalTestParameters() - if allMethods { - ret.Put = getTestOperation("put", opPrefix) - ret.Put.Parameters = getTestParameters() - ret.Post = getTestOperation("post", opPrefix) - ret.Post.Parameters = getTestParameters() - ret.Head = getTestOperation("head", opPrefix) - ret.Head.Parameters = getTestParameters() - ret.Patch = getTestOperation("patch", opPrefix) - ret.Patch.Parameters = getTestParameters() - ret.Delete = getTestOperation("delete", opPrefix) - ret.Delete.Parameters = getTestParameters() - ret.Options = getTestOperation("options", opPrefix) - ret.Options.Parameters = getTestParameters() - } - return ret -} - -func getRefSchema(ref string) *spec.Schema { - return &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: spec.MustCreateRef(ref), - }, - } -} - -func getTestResponses() *spec.Responses { - ret := spec.Responses{ - ResponsesProps: spec.ResponsesProps{ - StatusCodeResponses: map[int]spec.Response{}, - }, - } - ret.StatusCodeResponses[200] = spec.Response{ - ResponseProps: spec.ResponseProps{ - Description: "OK", - Schema: getRefSchema("#/definitions/openapi.TestOutput"), - }, - } - return &ret -} - -func getTestCommonParameters() []spec.Parameter { - ret := make([]spec.Parameter, 2) - ret[0] = spec.Parameter{ - SimpleSchema: spec.SimpleSchema{ - Type: "string", - }, - ParamProps: spec.ParamProps{ - Description: "path to the resource", - Name: "path", - In: "path", - Required: true, - }, - CommonValidations: spec.CommonValidations{ - UniqueItems: true, - }, - } - ret[1] = spec.Parameter{ - SimpleSchema: spec.SimpleSchema{ - Type: "string", - }, - ParamProps: spec.ParamProps{ - Description: "If 'true', then the output is pretty printed.", - Name: "pretty", - In: "query", - }, - CommonValidations: spec.CommonValidations{ - UniqueItems: true, - }, - } - return ret -} - -func getTestParameters() []spec.Parameter { - ret := make([]spec.Parameter, 1) - ret[0] = spec.Parameter{ - ParamProps: spec.ParamProps{ - Name: "body", - In: "body", - Required: true, - Schema: getRefSchema("#/definitions/openapi.TestInput"), - }, - } - return ret -} - -func getAdditionalTestParameters() []spec.Parameter { - ret := make([]spec.Parameter, 3) - ret[0] = spec.Parameter{ - ParamProps: spec.ParamProps{ - Name: "body", - In: "body", - Required: true, - Schema: getRefSchema("#/definitions/openapi.TestInput"), - }, - } - ret[1] = spec.Parameter{ - ParamProps: spec.ParamProps{ - Name: "fparam", - Description: "a test form parameter", - In: "formData", - }, - SimpleSchema: spec.SimpleSchema{ - Type: "number", - }, - CommonValidations: spec.CommonValidations{ - UniqueItems: true, - }, - } - ret[2] = spec.Parameter{ - SimpleSchema: spec.SimpleSchema{ - Type: "integer", - }, - ParamProps: spec.ParamProps{ - Description: "a test head parameter", - Name: "hparam", - In: "header", - }, - CommonValidations: spec.CommonValidations{ - UniqueItems: true, - }, - } - return ret -} - -func getTestInputDefinition() spec.Schema { - return spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Test input", - Properties: map[string]spec.Schema{ - "id": { - SchemaProps: spec.SchemaProps{ - Description: "ID of the input", - Type: spec.StringOrArray{"integer"}, - Format: "int32", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the input", - Type: spec.StringOrArray{"string"}, - }, - }, - "tags": { - SchemaProps: spec.SchemaProps{ - Type: spec.StringOrArray{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: spec.StringOrArray{"string"}, - }, - }, - }, - }, - }, - }, - }, - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-test": "test", - "x-test2": "test2", - }, - }, - } -} - -func getTestOutputDefinition() spec.Schema { - return spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Test output", - Properties: map[string]spec.Schema{ - "count": { - SchemaProps: spec.SchemaProps{ - Description: "Number of outputs", - Type: spec.StringOrArray{"integer"}, - Format: "int32", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the output", - Type: spec.StringOrArray{"string"}, - }, - }, - }, - }, - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-test2": "test2", - }, - }, - } -} - -func TestBuildSwaggerSpec(t *testing.T) { - o, container, assert := setUp(t, true) - expected := &spec.Swagger{ - SwaggerProps: spec.SwaggerProps{ - Info: &spec.Info{ - InfoProps: spec.InfoProps{ - Title: "TestAPI", - Description: "Test API", - Version: "unversioned", - }, - }, - Swagger: "2.0", - Paths: &spec.Paths{ - Paths: map[string]spec.PathItem{ - "/foo/test/{path}": getTestPathItem(true, "foo"), - "/bar/test/{path}": getTestPathItem(true, "bar"), - }, - }, - Definitions: spec.Definitions{ - "openapi.TestInput": getTestInputDefinition(), - "openapi.TestOutput": getTestOutputDefinition(), - }, - }, - } - err := o.init(container.RegisteredWebServices()) - if !assert.NoError(err) { - return - } - expected_json, err := json.Marshal(expected) - if !assert.NoError(err) { - return - } - actual_json, err := json.Marshal(o.swagger) - if !assert.NoError(err) { - return - } - assert.Equal(string(expected_json), string(actual_json)) -} diff --git a/pkg/server/openapi/util.go b/pkg/server/openapi/util.go deleted file mode 100644 index a4480cd50..000000000 --- a/pkg/server/openapi/util.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -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 openapi - -import ( - "sort" - - "github.com/emicklei/go-restful" - "github.com/go-openapi/spec" -) - -type parameters []spec.Parameter - -func (s parameters) Len() int { return len(s) } -func (s parameters) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// byNameIn used in sorting parameters by Name and In fields. -type byNameIn struct { - parameters -} - -func (s byNameIn) Less(i, j int) bool { - return s.parameters[i].Name < s.parameters[j].Name || (s.parameters[i].Name == s.parameters[j].Name && s.parameters[i].In < s.parameters[j].In) -} - -// SortParameters sorts parameters by Name and In fields. -func sortParameters(p []spec.Parameter) { - sort.Sort(byNameIn{p}) -} - -func groupRoutesByPath(routes []restful.Route) map[string][]restful.Route { - pathToRoutes := make(map[string][]restful.Route) - for _, r := range routes { - pathToRoutes[r.Path] = append(pathToRoutes[r.Path], r) - } - return pathToRoutes -} - -func mapKeyFromParam(param *restful.Parameter) interface{} { - return struct { - Name string - Kind int - }{ - Name: param.Data().Name, - Kind: param.Data().Kind, - } -} diff --git a/pkg/server/routes/openapi.go b/pkg/server/routes/openapi.go index 9ced93008..1bbfacf43 100644 --- a/pkg/server/routes/openapi.go +++ b/pkg/server/routes/openapi.go @@ -18,22 +18,21 @@ package routes import ( "github.com/emicklei/go-restful" - - "k8s.io/apimachinery/pkg/openapi" - "k8s.io/apiserver/pkg/server/mux" - apiserveropenapi "k8s.io/apiserver/pkg/server/openapi" - "github.com/golang/glog" + + "k8s.io/apiserver/pkg/server/mux" + "k8s.io/kube-openapi/pkg/common" + "k8s.io/kube-openapi/pkg/handler" ) // OpenAPI installs spec endpoints for each web service. type OpenAPI struct { - Config *openapi.Config + Config *common.Config } // Install adds the SwaggerUI webservice to the given mux. func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) { - err := apiserveropenapi.RegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, mux) + _, err := handler.BuildAndRegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, mux) if err != nil { glog.Fatalf("Failed to register open api spec for root: %v", err) } diff --git a/pkg/util/trie/BUILD b/pkg/util/trie/BUILD deleted file mode 100644 index 42aee264e..000000000 --- a/pkg/util/trie/BUILD +++ /dev/null @@ -1,14 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = ["trie.go"], - tags = ["automanaged"], -) diff --git a/pkg/util/trie/trie.go b/pkg/util/trie/trie.go deleted file mode 100644 index 90189e7a3..000000000 --- a/pkg/util/trie/trie.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -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 trie - -// A simple trie implementation with Add an HasPrefix methods only. -type Trie struct { - children map[byte]*Trie - wordTail bool - word string -} - -// New creates a Trie and add all strings in the provided list to it. -func New(list []string) Trie { - ret := Trie{ - children: make(map[byte]*Trie), - wordTail: false, - } - for _, v := range list { - ret.Add(v) - } - return ret -} - -// Add adds a word to this trie -func (t *Trie) Add(v string) { - root := t - for _, b := range []byte(v) { - child, exists := root.children[b] - if !exists { - child = &Trie{ - children: make(map[byte]*Trie), - wordTail: false, - } - root.children[b] = child - } - root = child - } - root.wordTail = true - root.word = v -} - -// HasPrefix returns true of v has any of the prefixes stored in this trie. -func (t *Trie) HasPrefix(v string) bool { - _, has := t.GetPrefix(v) - return has -} - -// GetPrefix is like HasPrefix but return the prefix in case of match or empty string otherwise. -func (t *Trie) GetPrefix(v string) (string, bool) { - root := t - if root.wordTail { - return root.word, true - } - for _, b := range []byte(v) { - child, exists := root.children[b] - if !exists { - return "", false - } - if child.wordTail { - return child.word, true - } - root = child - } - return "", false -}