build: move e2e dependencies into e2e/go.mod

Several packages are only used while running the e2e suite. These
packages are less important to update, as the they can not influence the
final executable that is part of the Ceph-CSI container-image.

By moving these dependencies out of the main Ceph-CSI go.mod, it is
easier to identify if a reported CVE affects Ceph-CSI, or only the
testing (like most of the Kubernetes CVEs).

Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
Niels de Vos
2025-03-04 08:57:28 +01:00
committed by mergify[bot]
parent 15da101b1b
commit bec6090996
8047 changed files with 1407827 additions and 3453 deletions

View File

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

View File

@ -1,468 +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 builder
import (
"encoding/json"
"fmt"
"net/http"
"strings"
restful "github.com/emicklei/go-restful/v3"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/common/restfuladapter"
"k8s.io/kube-openapi/pkg/util"
"k8s.io/kube-openapi/pkg/validation/spec"
)
const (
OpenAPIVersion = "2.0"
)
type openAPI struct {
config *common.Config
swagger *spec.Swagger
protocolList []string
definitions map[string]common.OpenAPIDefinition
}
// BuildOpenAPISpec builds OpenAPI spec given a list of route containers and common.Config to customize it.
//
// Deprecated: BuildOpenAPISpecFromRoutes should be used instead.
func BuildOpenAPISpec(routeContainers []*restful.WebService, config *common.Config) (*spec.Swagger, error) {
return BuildOpenAPISpecFromRoutes(restfuladapter.AdaptWebServices(routeContainers), config)
}
// BuildOpenAPISpecFromRoutes builds OpenAPI spec given a list of route containers and common.Config to customize it.
func BuildOpenAPISpecFromRoutes(routeContainers []common.RouteContainer, config *common.Config) (*spec.Swagger, error) {
o := newOpenAPI(config)
err := o.buildPaths(routeContainers)
if err != nil {
return nil, err
}
return o.finalizeSwagger()
}
// BuildOpenAPIDefinitionsForResource builds a partial OpenAPI spec given a sample object and common.Config to customize it.
func BuildOpenAPIDefinitionsForResource(model interface{}, config *common.Config) (*spec.Definitions, error) {
o := newOpenAPI(config)
// We can discard the return value of toSchema because all we care about is the side effect of calling it.
// All the models created for this resource get added to o.swagger.Definitions
_, err := o.toSchema(util.GetCanonicalTypeName(model))
if err != nil {
return nil, err
}
swagger, err := o.finalizeSwagger()
if err != nil {
return nil, err
}
return &swagger.Definitions, nil
}
// BuildOpenAPIDefinitionsForResources returns the OpenAPI spec which includes the definitions for the
// passed type names.
func BuildOpenAPIDefinitionsForResources(config *common.Config, names ...string) (*spec.Swagger, error) {
o := newOpenAPI(config)
// We can discard the return value of toSchema because all we care about is the side effect of calling it.
// All the models created for this resource get added to o.swagger.Definitions
for _, name := range names {
_, err := o.toSchema(name)
if err != nil {
return nil, err
}
}
return o.finalizeSwagger()
}
// newOpenAPI sets up the openAPI object so we can build the spec.
func newOpenAPI(config *common.Config) openAPI {
o := openAPI{
config: config,
swagger: &spec.Swagger{
SwaggerProps: spec.SwaggerProps{
Swagger: OpenAPIVersion,
Definitions: spec.Definitions{},
Responses: config.ResponseDefinitions,
Paths: &spec.Paths{Paths: map[string]spec.PathItem{}},
Info: config.Info,
},
},
}
if o.config.GetOperationIDAndTagsFromRoute == nil {
// Map the deprecated handler to the common interface, if provided.
if o.config.GetOperationIDAndTags != nil {
o.config.GetOperationIDAndTagsFromRoute = func(r common.Route) (string, []string, error) {
restfulRouteAdapter, ok := r.(*restfuladapter.RouteAdapter)
if !ok {
return "", nil, fmt.Errorf("config.GetOperationIDAndTags specified but route is not a restful v1 Route")
}
return o.config.GetOperationIDAndTags(restfulRouteAdapter.Route)
}
} else {
o.config.GetOperationIDAndTagsFromRoute = func(r common.Route) (string, []string, error) {
return r.OperationName(), 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/" + common.EscapeJsonPointer(defName))
})
if o.config.CommonResponses == nil {
o.config.CommonResponses = map[int]spec.Response{}
}
return o
}
// finalizeSwagger is called after the spec is built and returns the final spec.
// NOTE: finalizeSwagger also make changes to the final spec, as specified in the config.
func (o *openAPI) finalizeSwagger() (*spec.Swagger, error) {
if o.config.SecurityDefinitions != nil {
o.swagger.SecurityDefinitions = *o.config.SecurityDefinitions
o.swagger.Security = o.config.DefaultSecurity
}
if o.config.PostProcessSpec != nil {
var err error
o.swagger, err = o.config.PostProcessSpec(o.swagger)
if err != nil {
return nil, err
}
}
return deduplicateParameters(o.swagger)
}
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
}
}
if v, ok := item.Schema.Extensions[common.ExtensionV2Schema]; ok {
if v2Schema, isOpenAPISchema := v.(spec.Schema); isOpenAPISchema {
schema = v2Schema
}
}
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 its definition.
// This is the main function that keep track of definitions used in this spec and is depend on code generated
// by k8s.io/kubernetes/cmd/libs/go2idl/openapi-gen.
func (o *openAPI) buildDefinitionForType(name string) (string, error) {
if err := o.buildDefinitionRecursively(name); err != nil {
return "", err
}
defName, _ := o.config.GetDefinitionName(name)
return "#/definitions/" + common.EscapeJsonPointer(defName), nil
}
// buildPaths builds OpenAPI paths using go-restful's web services.
func (o *openAPI) buildPaths(routeContainers []common.RouteContainer) error {
pathsToIgnore := util.NewTrie(o.config.IgnorePrefixes)
duplicateOpId := make(map[string]string)
for _, w := range routeContainers {
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 common.Route, inPathCommonParamsMap map[interface{}]spec.Parameter) (ret *spec.Operation, err error) {
ret = &spec.Operation{
OperationProps: spec.OperationProps{
Description: route.Description(),
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, common.ExtensionPrefix) {
if ret.Extensions == nil {
ret.Extensions = spec.Extensions{}
}
ret.Extensions.Add(k, v)
}
}
if ret.ID, ret.Tags, err = o.config.GetOperationIDAndTagsFromRoute(route); err != nil {
return ret, err
}
// Build responses
for _, resp := range route.StatusCodeResponses() {
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.ResponsePayloadSample() != nil {
ret.Responses.StatusCodeResponses[http.StatusOK], err = o.buildResponse(route.ResponsePayloadSample(), "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.Parameters() {
if _, isCommon := inPathCommonParamsMap[mapKeyFromParam(param)]; !isCommon {
openAPIParam, err := o.buildParameter(param, route.RequestPayloadSample())
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(util.GetCanonicalTypeName(model))
if err != nil {
return spec.Response{}, err
}
return spec.Response{
ResponseProps: spec.ResponseProps{
Description: description,
Schema: schema,
},
}, nil
}
func (o *openAPI) findCommonParameters(routes []common.Route) (map[interface{}]spec.Parameter, error) {
commonParamsMap := make(map[interface{}]spec.Parameter, 0)
paramOpsCountByName := make(map[interface{}]int, 0)
paramNameKindToDataMap := make(map[interface{}]common.Parameter, 0)
for _, route := range routes {
routeParamDuplicateMap := make(map[interface{}]bool)
s := ""
params := route.Parameters()
for _, param := range params {
m, _ := json.Marshal(param)
s += string(m) + "\n"
key := mapKeyFromParam(param)
if routeParamDuplicateMap[key] {
msg, _ := json.Marshal(params)
return commonParamsMap, fmt.Errorf("duplicate parameter %v for route %v, %v", param.Name(), string(msg), s)
}
routeParamDuplicateMap[key] = true
paramOpsCountByName[key]++
paramNameKindToDataMap[key] = param
}
}
for key, count := range paramOpsCountByName {
paramData := paramNameKindToDataMap[key]
if count == len(routes) && paramData.Kind() != common.BodyParameterKind {
openAPIParam, err := o.buildParameter(paramData, nil)
if err != nil {
return commonParamsMap, err
}
commonParamsMap[key] = openAPIParam
}
}
return commonParamsMap, nil
}
func (o *openAPI) toSchema(name string) (_ *spec.Schema, err error) {
if openAPIType, openAPIFormat := common.OpenAPITypeFormat(name); openAPIType != "" {
return &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{openAPIType},
Format: openAPIFormat,
},
}, nil
} else {
ref, err := o.buildDefinitionForType(name)
if err != nil {
return nil, err
}
return &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef(ref),
},
}, nil
}
}
func (o *openAPI) buildParameter(restParam common.Parameter, 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 common.BodyParameterKind:
if bodySample != nil {
ret.In = "body"
ret.Schema, err = o.toSchema(util.GetCanonicalTypeName(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 common.PathParameterKind:
ret.In = "path"
if !restParam.Required() {
return ret, fmt.Errorf("path parameters should be marked at required for parameter %v", restParam)
}
case common.QueryParameterKind:
ret.In = "query"
case common.HeaderParameterKind:
ret.In = "header"
case common.FormParameterKind:
ret.In = "formData"
default:
return ret, fmt.Errorf("unknown restful operation kind : %v", restParam.Kind())
}
openAPIType, openAPIFormat := common.OpenAPITypeFormat(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 []common.Parameter) (ret []spec.Parameter, err error) {
ret = make([]spec.Parameter, len(restParam))
for i, v := range restParam {
ret[i], err = o.buildParameter(v, nil)
if err != nil {
return ret, err
}
}
return ret, nil
}

View File

@ -1,259 +0,0 @@
/*
Copyright 2023 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 builder
import (
"encoding/base64"
"encoding/json"
"fmt"
"hash/fnv"
"sort"
"strconv"
"strings"
"k8s.io/kube-openapi/pkg/validation/spec"
)
// deduplicateParameters finds parameters that are shared across multiple endpoints and replace them with
// references to the shared parameters in order to avoid repetition.
//
// deduplicateParameters does not mutate the source.
func deduplicateParameters(sp *spec.Swagger) (*spec.Swagger, error) {
names, parameters, err := collectSharedParameters(sp)
if err != nil {
return nil, err
}
if sp.Parameters != nil {
return nil, fmt.Errorf("shared parameters already exist") // should not happen with the builder, but to be sure
}
clone := *sp
clone.Parameters = parameters
return replaceSharedParameters(names, &clone)
}
// collectSharedParameters finds parameters that show up for many endpoints. These
// are basically all parameters with the exceptions of those where we know they are
// endpoint specific, e.g. because they reference the schema of the kind, or have
// the kind or resource name in the description.
func collectSharedParameters(sp *spec.Swagger) (namesByJSON map[string]string, ret map[string]spec.Parameter, err error) {
if sp == nil || sp.Paths == nil {
return nil, nil, nil
}
countsByJSON := map[string]int{}
shared := map[string]spec.Parameter{}
var keys []string
collect := func(p *spec.Parameter) error {
if (p.In == "query" || p.In == "path") && p.Name == "name" {
return nil // ignore name parameter as they are never shared with the Kind in the description
}
if p.In == "query" && p.Name == "fieldValidation" {
return nil // keep fieldValidation parameter unshared because kubectl uses it (until 1.27) to detect server-side field validation support
}
if p.In == "query" && p.Name == "dryRun" {
return nil // keep fieldValidation parameter unshared because kubectl uses it (until 1.26) to detect dry-run support
}
if p.Schema != nil && p.In == "body" && p.Name == "body" && !strings.HasPrefix(p.Schema.Ref.String(), "#/definitions/io.k8s.apimachinery") {
return nil // ignore non-generic body parameters as they reference the custom schema of the kind
}
bs, err := json.Marshal(p)
if err != nil {
return err
}
k := string(bs)
countsByJSON[k]++
if count := countsByJSON[k]; count == 1 {
shared[k] = *p
keys = append(keys, k)
}
return nil
}
for _, path := range sp.Paths.Paths {
// per operation parameters
for _, op := range operations(&path) {
if op == nil {
continue // shouldn't happen, but ignore if it does; tested through unit test
}
for _, p := range op.Parameters {
if p.Ref.String() != "" {
// shouldn't happen, but ignore if it does
continue
}
if err := collect(&p); err != nil {
return nil, nil, err
}
}
}
// per path parameters
for _, p := range path.Parameters {
if p.Ref.String() != "" {
continue // shouldn't happen, but ignore if it does
}
if err := collect(&p); err != nil {
return nil, nil, err
}
}
}
// name deterministically
sort.Strings(keys)
ret = map[string]spec.Parameter{}
namesByJSON = map[string]string{}
for _, k := range keys {
name := shared[k].Name
if name == "" {
// this should never happen as the name is a required field. But if it does, let's be safe.
name = "param"
}
name += "-" + base64Hash(k)
i := 0
for {
if _, ok := ret[name]; !ok {
ret[name] = shared[k]
namesByJSON[k] = name
break
}
i++ // only on hash conflict, unlikely with our few variants
name = shared[k].Name + "-" + strconv.Itoa(i)
}
}
return namesByJSON, ret, nil
}
func operations(path *spec.PathItem) []*spec.Operation {
return []*spec.Operation{path.Get, path.Put, path.Post, path.Delete, path.Options, path.Head, path.Patch}
}
func base64Hash(s string) string {
hash := fnv.New64()
hash.Write([]byte(s)) //nolint:errcheck
return base64.URLEncoding.EncodeToString(hash.Sum(make([]byte, 0, 8))[:6]) // 8 characters
}
func replaceSharedParameters(sharedParameterNamesByJSON map[string]string, sp *spec.Swagger) (*spec.Swagger, error) {
if sp == nil || sp.Paths == nil {
return sp, nil
}
ret := sp
firstPathChange := true
for k, path := range sp.Paths.Paths {
pathChanged := false
// per operation parameters
for _, op := range []**spec.Operation{&path.Get, &path.Put, &path.Post, &path.Delete, &path.Options, &path.Head, &path.Patch} {
if *op == nil {
continue
}
firstParamChange := true
for i := range (*op).Parameters {
p := (*op).Parameters[i]
if p.Ref.String() != "" {
// shouldn't happen, but be idem-potent if it does
continue
}
bs, err := json.Marshal(p)
if err != nil {
return nil, err
}
if name, ok := sharedParameterNamesByJSON[string(bs)]; ok {
if firstParamChange {
orig := *op
*op = &spec.Operation{}
**op = *orig
(*op).Parameters = make([]spec.Parameter, len(orig.Parameters))
copy((*op).Parameters, orig.Parameters)
firstParamChange = false
}
(*op).Parameters[i] = spec.Parameter{
Refable: spec.Refable{
Ref: spec.MustCreateRef("#/parameters/" + name),
},
}
pathChanged = true
}
}
}
// per path parameters
firstParamChange := true
for i := range path.Parameters {
p := path.Parameters[i]
if p.Ref.String() != "" {
// shouldn't happen, but be idem-potent if it does
continue
}
bs, err := json.Marshal(p)
if err != nil {
return nil, err
}
if name, ok := sharedParameterNamesByJSON[string(bs)]; ok {
if firstParamChange {
orig := path.Parameters
path.Parameters = make([]spec.Parameter, len(orig))
copy(path.Parameters, orig)
firstParamChange = false
}
path.Parameters[i] = spec.Parameter{
Refable: spec.Refable{
Ref: spec.MustCreateRef("#/parameters/" + name),
},
}
pathChanged = true
}
}
if pathChanged {
if firstPathChange {
clone := *sp
ret = &clone
pathsClone := *ret.Paths
ret.Paths = &pathsClone
ret.Paths.Paths = make(map[string]spec.PathItem, len(sp.Paths.Paths))
for k, v := range sp.Paths.Paths {
ret.Paths.Paths[k] = v
}
firstPathChange = false
}
ret.Paths.Paths[k] = path
}
}
return ret, nil
}

View File

@ -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 builder
import (
"sort"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/validation/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 []common.Route) map[string][]common.Route {
pathToRoutes := make(map[string][]common.Route)
for _, r := range routes {
pathToRoutes[r.Path()] = append(pathToRoutes[r.Path()], r)
}
return pathToRoutes
}
func mapKeyFromParam(param common.Parameter) interface{} {
return struct {
Name string
Kind common.ParameterKind
}{
Name: param.Name(),
Kind: param.Kind(),
}
}

View File

@ -1,498 +0,0 @@
/*
Copyright 2021 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 builder3
import (
"encoding/json"
"fmt"
"net/http"
"strings"
restful "github.com/emicklei/go-restful/v3"
builderutil "k8s.io/kube-openapi/pkg/builder3/util"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/common/restfuladapter"
"k8s.io/kube-openapi/pkg/spec3"
"k8s.io/kube-openapi/pkg/util"
"k8s.io/kube-openapi/pkg/validation/spec"
)
const (
OpenAPIVersion = "3.0"
)
type openAPI struct {
config *common.OpenAPIV3Config
spec *spec3.OpenAPI
definitions map[string]common.OpenAPIDefinition
}
func groupRoutesByPath(routes []common.Route) map[string][]common.Route {
pathToRoutes := make(map[string][]common.Route)
for _, r := range routes {
pathToRoutes[r.Path()] = append(pathToRoutes[r.Path()], r)
}
return pathToRoutes
}
func (o *openAPI) buildResponse(model interface{}, description string, content []string) (*spec3.Response, error) {
response := &spec3.Response{
ResponseProps: spec3.ResponseProps{
Description: description,
Content: make(map[string]*spec3.MediaType),
},
}
s, err := o.toSchema(util.GetCanonicalTypeName(model))
if err != nil {
return nil, err
}
for _, contentType := range content {
response.ResponseProps.Content[contentType] = &spec3.MediaType{
MediaTypeProps: spec3.MediaTypeProps{
Schema: s,
},
}
}
return response, nil
}
func (o *openAPI) buildOperations(route common.Route, inPathCommonParamsMap map[interface{}]*spec3.Parameter) (*spec3.Operation, error) {
ret := &spec3.Operation{
OperationProps: spec3.OperationProps{
Description: route.Description(),
Responses: &spec3.Responses{
ResponsesProps: spec3.ResponsesProps{
StatusCodeResponses: make(map[int]*spec3.Response),
},
},
},
}
for k, v := range route.Metadata() {
if strings.HasPrefix(k, common.ExtensionPrefix) {
if ret.Extensions == nil {
ret.Extensions = spec.Extensions{}
}
ret.Extensions.Add(k, v)
}
}
var err error
if ret.OperationId, ret.Tags, err = o.config.GetOperationIDAndTagsFromRoute(route); err != nil {
return ret, err
}
// Build responses
for _, resp := range route.StatusCodeResponses() {
ret.Responses.StatusCodeResponses[resp.Code()], err = o.buildResponse(resp.Model(), resp.Message(), route.Produces())
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.ResponsePayloadSample() != nil {
ret.Responses.StatusCodeResponses[http.StatusOK], err = o.buildResponse(route.ResponsePayloadSample(), "OK", route.Produces())
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 len(ret.Responses.StatusCodeResponses) == 0 {
ret.Responses.Default = o.config.DefaultResponse
}
params := route.Parameters()
for _, param := range params {
_, isCommon := inPathCommonParamsMap[mapKeyFromParam(param)]
if !isCommon && param.Kind() != common.BodyParameterKind {
openAPIParam, err := o.buildParameter(param)
if err != nil {
return ret, err
}
ret.Parameters = append(ret.Parameters, openAPIParam)
}
}
body, err := o.buildRequestBody(params, route.Consumes(), route.RequestPayloadSample())
if err != nil {
return nil, err
}
if body != nil {
ret.RequestBody = body
}
return ret, nil
}
func (o *openAPI) buildRequestBody(parameters []common.Parameter, consumes []string, bodySample interface{}) (*spec3.RequestBody, error) {
for _, param := range parameters {
if param.Kind() == common.BodyParameterKind && bodySample != nil {
schema, err := o.toSchema(util.GetCanonicalTypeName(bodySample))
if err != nil {
return nil, err
}
r := &spec3.RequestBody{
RequestBodyProps: spec3.RequestBodyProps{
Content: map[string]*spec3.MediaType{},
Description: param.Description(),
Required: param.Required(),
},
}
for _, consume := range consumes {
r.Content[consume] = &spec3.MediaType{
MediaTypeProps: spec3.MediaTypeProps{
Schema: schema,
},
}
}
return r, nil
}
}
return nil, nil
}
func newOpenAPI(config *common.OpenAPIV3Config) openAPI {
o := openAPI{
config: config,
spec: &spec3.OpenAPI{
Version: "3.0.0",
Info: config.Info,
Paths: &spec3.Paths{
Paths: map[string]*spec3.Path{},
},
Components: &spec3.Components{
Schemas: map[string]*spec.Schema{},
},
},
}
if len(o.config.ResponseDefinitions) > 0 {
o.spec.Components.Responses = make(map[string]*spec3.Response)
}
for k, response := range o.config.ResponseDefinitions {
o.spec.Components.Responses[k] = response
}
if len(o.config.SecuritySchemes) > 0 {
o.spec.Components.SecuritySchemes = make(spec3.SecuritySchemes)
}
for k, securityScheme := range o.config.SecuritySchemes {
o.spec.Components.SecuritySchemes[k] = securityScheme
}
if o.config.GetOperationIDAndTagsFromRoute == nil {
// Map the deprecated handler to the common interface, if provided.
if o.config.GetOperationIDAndTags != nil {
o.config.GetOperationIDAndTagsFromRoute = func(r common.Route) (string, []string, error) {
restfulRouteAdapter, ok := r.(*restfuladapter.RouteAdapter)
if !ok {
return "", nil, fmt.Errorf("config.GetOperationIDAndTags specified but route is not a restful v1 Route")
}
return o.config.GetOperationIDAndTags(restfulRouteAdapter.Route)
}
} else {
o.config.GetOperationIDAndTagsFromRoute = func(r common.Route) (string, []string, error) {
return r.OperationName(), nil, nil
}
}
}
if o.config.GetDefinitionName == nil {
o.config.GetDefinitionName = func(name string) (string, spec.Extensions) {
return name[strings.LastIndex(name, "/")+1:], nil
}
}
if o.config.Definitions != nil {
o.definitions = o.config.Definitions
} else {
o.definitions = o.config.GetDefinitions(func(name string) spec.Ref {
defName, _ := o.config.GetDefinitionName(name)
return spec.MustCreateRef("#/components/schemas/" + common.EscapeJsonPointer(defName))
})
}
return o
}
func (o *openAPI) buildOpenAPISpec(webServices []common.RouteContainer) error {
pathsToIgnore := util.NewTrie(o.config.IgnorePrefixes)
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.spec.Paths.Paths[path]
if exists {
return fmt.Errorf("duplicate webservice route has been found for path: %v", path)
}
pathItem = &spec3.Path{
PathProps: spec3.PathProps{},
}
// 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)
if err != nil {
return err
}
sortParameters(op.Parameters)
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.spec.Paths.Paths[path] = pathItem
}
}
return nil
}
// BuildOpenAPISpec builds OpenAPI v3 spec given a list of route containers and common.Config to customize it.
//
// Deprecated: BuildOpenAPISpecFromRoutes should be used instead.
func BuildOpenAPISpec(webServices []*restful.WebService, config *common.OpenAPIV3Config) (*spec3.OpenAPI, error) {
return BuildOpenAPISpecFromRoutes(restfuladapter.AdaptWebServices(webServices), config)
}
// BuildOpenAPISpecFromRoutes builds OpenAPI v3 spec given a list of route containers and common.Config to customize it.
func BuildOpenAPISpecFromRoutes(webServices []common.RouteContainer, config *common.OpenAPIV3Config) (*spec3.OpenAPI, error) {
a := newOpenAPI(config)
err := a.buildOpenAPISpec(webServices)
if err != nil {
return nil, err
}
if config.PostProcessSpec != nil {
return config.PostProcessSpec(a.spec)
}
return a.spec, nil
}
// BuildOpenAPIDefinitionsForResource builds a partial OpenAPI spec given a sample object and common.Config to customize it.
// BuildOpenAPIDefinitionsForResources returns the OpenAPI spec which includes the definitions for the
// passed type names.
func BuildOpenAPIDefinitionsForResources(config *common.OpenAPIV3Config, names ...string) (map[string]*spec.Schema, error) {
o := newOpenAPI(config)
// We can discard the return value of toSchema because all we care about is the side effect of calling it.
// All the models created for this resource get added to o.swagger.Definitions
for _, name := range names {
_, err := o.toSchema(name)
if err != nil {
return nil, err
}
}
return o.spec.Components.Schemas, nil
}
func (o *openAPI) findCommonParameters(routes []common.Route) (map[interface{}]*spec3.Parameter, error) {
commonParamsMap := make(map[interface{}]*spec3.Parameter, 0)
paramOpsCountByName := make(map[interface{}]int, 0)
paramNameKindToDataMap := make(map[interface{}]common.Parameter, 0)
for _, route := range routes {
routeParamDuplicateMap := make(map[interface{}]bool)
s := ""
params := route.Parameters()
for _, param := range params {
m, _ := json.Marshal(param)
s += string(m) + "\n"
key := mapKeyFromParam(param)
if routeParamDuplicateMap[key] {
msg, _ := json.Marshal(params)
return commonParamsMap, fmt.Errorf("duplicate parameter %v for route %v, %v", param.Name(), string(msg), s)
}
routeParamDuplicateMap[key] = true
paramOpsCountByName[key]++
paramNameKindToDataMap[key] = param
}
}
for key, count := range paramOpsCountByName {
paramData := paramNameKindToDataMap[key]
if count == len(routes) && paramData.Kind() != common.BodyParameterKind {
openAPIParam, err := o.buildParameter(paramData)
if err != nil {
return commonParamsMap, err
}
commonParamsMap[key] = openAPIParam
}
}
return commonParamsMap, nil
}
func (o *openAPI) buildParameters(restParam []common.Parameter) (ret []*spec3.Parameter, err error) {
ret = make([]*spec3.Parameter, len(restParam))
for i, v := range restParam {
ret[i], err = o.buildParameter(v)
if err != nil {
return ret, err
}
}
return ret, nil
}
func (o *openAPI) buildParameter(restParam common.Parameter) (ret *spec3.Parameter, err error) {
ret = &spec3.Parameter{
ParameterProps: spec3.ParameterProps{
Name: restParam.Name(),
Description: restParam.Description(),
Required: restParam.Required(),
},
}
switch restParam.Kind() {
case common.BodyParameterKind:
return nil, nil
case common.PathParameterKind:
ret.In = "path"
if !restParam.Required() {
return ret, fmt.Errorf("path parameters should be marked as required for parameter %v", restParam)
}
case common.QueryParameterKind:
ret.In = "query"
case common.HeaderParameterKind:
ret.In = "header"
/* TODO: add support for the cookie param */
default:
return ret, fmt.Errorf("unsupported restful parameter kind : %v", restParam.Kind())
}
openAPIType, openAPIFormat := common.OpenAPITypeFormat(restParam.DataType())
if openAPIType == "" {
return ret, fmt.Errorf("non-body Restful parameter type should be a simple type, but got : %v", restParam.DataType())
}
ret.Schema = &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{openAPIType},
Format: openAPIFormat,
UniqueItems: !restParam.AllowMultiple(),
},
}
return ret, nil
}
func (o *openAPI) buildDefinitionRecursively(name string) error {
uniqueName, extensions := o.config.GetDefinitionName(name)
if _, ok := o.spec.Components.Schemas[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
}
}
// delete the embedded v2 schema if exists, otherwise no-op
delete(schema.VendorExtensible.Extensions, common.ExtensionV2Schema)
schema = builderutil.WrapRefs(schema)
o.spec.Components.Schemas[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
}
func (o *openAPI) buildDefinitionForType(name string) (string, error) {
if err := o.buildDefinitionRecursively(name); err != nil {
return "", err
}
defName, _ := o.config.GetDefinitionName(name)
return "#/components/schemas/" + common.EscapeJsonPointer(defName), nil
}
func (o *openAPI) toSchema(name string) (_ *spec.Schema, err error) {
if openAPIType, openAPIFormat := common.OpenAPITypeFormat(name); openAPIType != "" {
return &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{openAPIType},
Format: openAPIFormat,
},
}, nil
} else {
ref, err := o.buildDefinitionForType(name)
if err != nil {
return nil, err
}
return &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef(ref),
},
}, nil
}
}

View File

@ -1,52 +0,0 @@
/*
Copyright 2021 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 builder3
import (
"sort"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/spec3"
)
func mapKeyFromParam(param common.Parameter) interface{} {
return struct {
Name string
Kind common.ParameterKind
}{
Name: param.Name(),
Kind: param.Kind(),
}
}
func (s parameters) Len() int { return len(s) }
func (s parameters) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type parameters []*spec3.Parameter
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 []*spec3.Parameter) {
sort.Sort(byNameIn{p})
}

View File

@ -1,51 +0,0 @@
/*
Copyright 2022 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 util
import (
"reflect"
"k8s.io/kube-openapi/pkg/schemamutation"
"k8s.io/kube-openapi/pkg/validation/spec"
)
// wrapRefs wraps OpenAPI V3 Schema refs that contain sibling elements.
// AllOf is used to wrap the Ref to prevent references from having sibling elements
// Please see https://github.com/kubernetes/kubernetes/issues/106387#issuecomment-967640388
func WrapRefs(schema *spec.Schema) *spec.Schema {
walker := schemamutation.Walker{
SchemaCallback: func(schema *spec.Schema) *spec.Schema {
orig := schema
clone := func() {
if orig == schema {
schema = new(spec.Schema)
*schema = *orig
}
}
if schema.Ref.String() != "" && !reflect.DeepEqual(*schema, spec.Schema{SchemaProps: spec.SchemaProps{Ref: schema.Ref}}) {
clone()
refSchema := new(spec.Schema)
refSchema.Ref = schema.Ref
schema.Ref = spec.Ref{}
schema.AllOf = []spec.Schema{*refSchema}
}
return schema
},
RefCallback: schemamutation.RefCallbackNoop,
}
return walker.WalkSchema(schema)
}

View File

@ -1,15 +0,0 @@
package restfuladapter
import (
"github.com/emicklei/go-restful/v3"
"k8s.io/kube-openapi/pkg/common"
)
// AdaptWebServices adapts a slice of restful.WebService into the common interfaces.
func AdaptWebServices(webServices []*restful.WebService) []common.RouteContainer {
var containers []common.RouteContainer
for _, ws := range webServices {
containers = append(containers, &WebServiceAdapter{ws})
}
return containers
}

View File

@ -1,54 +0,0 @@
package restfuladapter
import (
"encoding/json"
"github.com/emicklei/go-restful/v3"
"k8s.io/kube-openapi/pkg/common"
)
var _ common.Parameter = &ParamAdapter{}
type ParamAdapter struct {
Param *restful.Parameter
}
func (r *ParamAdapter) MarshalJSON() ([]byte, error) {
return json.Marshal(r.Param)
}
func (r *ParamAdapter) Name() string {
return r.Param.Data().Name
}
func (r *ParamAdapter) Description() string {
return r.Param.Data().Description
}
func (r *ParamAdapter) Required() bool {
return r.Param.Data().Required
}
func (r *ParamAdapter) Kind() common.ParameterKind {
switch r.Param.Kind() {
case restful.PathParameterKind:
return common.PathParameterKind
case restful.QueryParameterKind:
return common.QueryParameterKind
case restful.BodyParameterKind:
return common.BodyParameterKind
case restful.HeaderParameterKind:
return common.HeaderParameterKind
case restful.FormParameterKind:
return common.FormParameterKind
default:
return common.UnknownParameterKind
}
}
func (r *ParamAdapter) DataType() string {
return r.Param.Data().DataType
}
func (r *ParamAdapter) AllowMultiple() bool {
return r.Param.Data().AllowMultiple
}

View File

@ -1,25 +0,0 @@
package restfuladapter
import (
"github.com/emicklei/go-restful/v3"
"k8s.io/kube-openapi/pkg/common"
)
var _ common.StatusCodeResponse = &ResponseErrorAdapter{}
// ResponseErrorAdapter adapts a restful.ResponseError to common.StatusCodeResponse.
type ResponseErrorAdapter struct {
Err *restful.ResponseError
}
func (r *ResponseErrorAdapter) Message() string {
return r.Err.Message
}
func (r *ResponseErrorAdapter) Model() interface{} {
return r.Err.Model
}
func (r *ResponseErrorAdapter) Code() int {
return r.Err.Code
}

View File

@ -1,68 +0,0 @@
package restfuladapter
import (
"github.com/emicklei/go-restful/v3"
"k8s.io/kube-openapi/pkg/common"
)
var _ common.Route = &RouteAdapter{}
// RouteAdapter adapts a restful.Route to common.Route.
type RouteAdapter struct {
Route *restful.Route
}
func (r *RouteAdapter) StatusCodeResponses() []common.StatusCodeResponse {
// go-restful uses the ResponseErrors field to contain both error and regular responses.
var responses []common.StatusCodeResponse
for _, res := range r.Route.ResponseErrors {
localRes := res
responses = append(responses, &ResponseErrorAdapter{&localRes})
}
return responses
}
func (r *RouteAdapter) OperationName() string {
return r.Route.Operation
}
func (r *RouteAdapter) Method() string {
return r.Route.Method
}
func (r *RouteAdapter) Path() string {
return r.Route.Path
}
func (r *RouteAdapter) Parameters() []common.Parameter {
var params []common.Parameter
for _, rParam := range r.Route.ParameterDocs {
params = append(params, &ParamAdapter{rParam})
}
return params
}
func (r *RouteAdapter) Description() string {
return r.Route.Doc
}
func (r *RouteAdapter) Consumes() []string {
return r.Route.Consumes
}
func (r *RouteAdapter) Produces() []string {
return r.Route.Produces
}
func (r *RouteAdapter) Metadata() map[string]interface{} {
return r.Route.Metadata
}
func (r *RouteAdapter) RequestPayloadSample() interface{} {
return r.Route.ReadSample
}
func (r *RouteAdapter) ResponsePayloadSample() interface{} {
return r.Route.WriteSample
}

View File

@ -1,34 +0,0 @@
package restfuladapter
import (
"github.com/emicklei/go-restful/v3"
"k8s.io/kube-openapi/pkg/common"
)
var _ common.RouteContainer = &WebServiceAdapter{}
// WebServiceAdapter adapts a restful.WebService to common.RouteContainer.
type WebServiceAdapter struct {
WebService *restful.WebService
}
func (r *WebServiceAdapter) RootPath() string {
return r.WebService.RootPath()
}
func (r *WebServiceAdapter) PathParameters() []common.Parameter {
var params []common.Parameter
for _, rParam := range r.WebService.PathParameters() {
params = append(params, &ParamAdapter{rParam})
}
return params
}
func (r *WebServiceAdapter) Routes() []common.Route {
var routes []common.Route
for _, rRoute := range r.WebService.Routes() {
localRoute := rRoute
routes = append(routes, &RouteAdapter{&localRoute})
}
return routes
}

View File

@ -1,208 +0,0 @@
/*
Copyright 2020 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 handler
import "k8s.io/kube-openapi/pkg/validation/spec"
// PruneDefaults remove all the defaults recursively from all the
// schemas in the definitions, and does not modify the definitions in
// place.
func PruneDefaults(definitions spec.Definitions) spec.Definitions {
definitionsCloned := false
for k, v := range definitions {
if s := PruneDefaultsSchema(&v); s != &v {
if !definitionsCloned {
definitionsCloned = true
orig := definitions
definitions = make(spec.Definitions, len(orig))
for k2, v2 := range orig {
definitions[k2] = v2
}
}
definitions[k] = *s
}
}
return definitions
}
// PruneDefaultsSchema remove all the defaults recursively from the
// schema in place.
func PruneDefaultsSchema(schema *spec.Schema) *spec.Schema {
if schema == nil {
return nil
}
orig := schema
clone := func() {
if orig == schema {
schema = &spec.Schema{}
*schema = *orig
}
}
if schema.Default != nil {
clone()
schema.Default = nil
}
definitionsCloned := false
for k, v := range schema.Definitions {
if s := PruneDefaultsSchema(&v); s != &v {
if !definitionsCloned {
definitionsCloned = true
clone()
schema.Definitions = make(spec.Definitions, len(orig.Definitions))
for k2, v2 := range orig.Definitions {
schema.Definitions[k2] = v2
}
}
schema.Definitions[k] = *s
}
}
propertiesCloned := false
for k, v := range schema.Properties {
if s := PruneDefaultsSchema(&v); s != &v {
if !propertiesCloned {
propertiesCloned = true
clone()
schema.Properties = make(map[string]spec.Schema, len(orig.Properties))
for k2, v2 := range orig.Properties {
schema.Properties[k2] = v2
}
}
schema.Properties[k] = *s
}
}
patternPropertiesCloned := false
for k, v := range schema.PatternProperties {
if s := PruneDefaultsSchema(&v); s != &v {
if !patternPropertiesCloned {
patternPropertiesCloned = true
clone()
schema.PatternProperties = make(map[string]spec.Schema, len(orig.PatternProperties))
for k2, v2 := range orig.PatternProperties {
schema.PatternProperties[k2] = v2
}
}
schema.PatternProperties[k] = *s
}
}
dependenciesCloned := false
for k, v := range schema.Dependencies {
if s := PruneDefaultsSchema(v.Schema); s != v.Schema {
if !dependenciesCloned {
dependenciesCloned = true
clone()
schema.Dependencies = make(spec.Dependencies, len(orig.Dependencies))
for k2, v2 := range orig.Dependencies {
schema.Dependencies[k2] = v2
}
}
v.Schema = s
schema.Dependencies[k] = v
}
}
allOfCloned := false
for i := range schema.AllOf {
if s := PruneDefaultsSchema(&schema.AllOf[i]); s != &schema.AllOf[i] {
if !allOfCloned {
allOfCloned = true
clone()
schema.AllOf = make([]spec.Schema, len(orig.AllOf))
copy(schema.AllOf, orig.AllOf)
}
schema.AllOf[i] = *s
}
}
anyOfCloned := false
for i := range schema.AnyOf {
if s := PruneDefaultsSchema(&schema.AnyOf[i]); s != &schema.AnyOf[i] {
if !anyOfCloned {
anyOfCloned = true
clone()
schema.AnyOf = make([]spec.Schema, len(orig.AnyOf))
copy(schema.AnyOf, orig.AnyOf)
}
schema.AnyOf[i] = *s
}
}
oneOfCloned := false
for i := range schema.OneOf {
if s := PruneDefaultsSchema(&schema.OneOf[i]); s != &schema.OneOf[i] {
if !oneOfCloned {
oneOfCloned = true
clone()
schema.OneOf = make([]spec.Schema, len(orig.OneOf))
copy(schema.OneOf, orig.OneOf)
}
schema.OneOf[i] = *s
}
}
if schema.Not != nil {
if s := PruneDefaultsSchema(schema.Not); s != schema.Not {
clone()
schema.Not = s
}
}
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
if s := PruneDefaultsSchema(schema.AdditionalProperties.Schema); s != schema.AdditionalProperties.Schema {
clone()
schema.AdditionalProperties = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalProperties.Allows}
}
}
if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
if s := PruneDefaultsSchema(schema.AdditionalItems.Schema); s != schema.AdditionalItems.Schema {
clone()
schema.AdditionalItems = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalItems.Allows}
}
}
if schema.Items != nil {
if schema.Items.Schema != nil {
if s := PruneDefaultsSchema(schema.Items.Schema); s != schema.Items.Schema {
clone()
schema.Items = &spec.SchemaOrArray{Schema: s}
}
} else {
itemsCloned := false
for i := range schema.Items.Schemas {
if s := PruneDefaultsSchema(&schema.Items.Schemas[i]); s != &schema.Items.Schemas[i] {
if !itemsCloned {
clone()
schema.Items = &spec.SchemaOrArray{
Schemas: make([]spec.Schema, len(orig.Items.Schemas)),
}
itemsCloned = true
copy(schema.Items.Schemas, orig.Items.Schemas)
}
schema.Items.Schemas[i] = *s
}
}
}
}
return schema
}

View File

@ -1,202 +0,0 @@
/*
Copyright 2017 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 handler
import (
"bytes"
"crypto/sha512"
"fmt"
"net/http"
"strconv"
"time"
"github.com/NYTimes/gziphandler"
"github.com/emicklei/go-restful/v3"
openapi_v2 "github.com/google/gnostic-models/openapiv2"
"github.com/google/uuid"
"github.com/munnerz/goautoneg"
"google.golang.org/protobuf/proto"
klog "k8s.io/klog/v2"
"k8s.io/kube-openapi/pkg/builder"
"k8s.io/kube-openapi/pkg/cached"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/common/restfuladapter"
"k8s.io/kube-openapi/pkg/validation/spec"
)
const (
subTypeProtobufDeprecated = "com.github.proto-openapi.spec.v2@v1.0+protobuf"
subTypeProtobuf = "com.github.proto-openapi.spec.v2.v1.0+protobuf"
subTypeJSON = "json"
)
func computeETag(data []byte) string {
if data == nil {
return ""
}
return fmt.Sprintf("%X", sha512.Sum512(data))
}
type timedSpec struct {
spec []byte
lastModified time.Time
}
// OpenAPIService is the service responsible for serving OpenAPI spec. It has
// the ability to safely change the spec while serving it.
type OpenAPIService struct {
specCache cached.LastSuccess[*spec.Swagger]
jsonCache cached.Value[timedSpec]
protoCache cached.Value[timedSpec]
}
// NewOpenAPIService builds an OpenAPIService starting with the given spec.
func NewOpenAPIService(swagger *spec.Swagger) *OpenAPIService {
return NewOpenAPIServiceLazy(cached.Static(swagger, uuid.New().String()))
}
// NewOpenAPIServiceLazy builds an OpenAPIService from lazy spec.
func NewOpenAPIServiceLazy(swagger cached.Value[*spec.Swagger]) *OpenAPIService {
o := &OpenAPIService{}
o.UpdateSpecLazy(swagger)
o.jsonCache = cached.Transform[*spec.Swagger](func(spec *spec.Swagger, etag string, err error) (timedSpec, string, error) {
if err != nil {
return timedSpec{}, "", err
}
json, err := spec.MarshalJSON()
if err != nil {
return timedSpec{}, "", err
}
return timedSpec{spec: json, lastModified: time.Now()}, computeETag(json), nil
}, &o.specCache)
o.protoCache = cached.Transform(func(ts timedSpec, etag string, err error) (timedSpec, string, error) {
if err != nil {
return timedSpec{}, "", err
}
proto, err := ToProtoBinary(ts.spec)
if err != nil {
return timedSpec{}, "", err
}
// We can re-use the same etag as json because of the Vary header.
return timedSpec{spec: proto, lastModified: ts.lastModified}, etag, nil
}, o.jsonCache)
return o
}
func (o *OpenAPIService) UpdateSpec(swagger *spec.Swagger) error {
o.UpdateSpecLazy(cached.Static(swagger, uuid.New().String()))
return nil
}
func (o *OpenAPIService) UpdateSpecLazy(swagger cached.Value[*spec.Swagger]) {
o.specCache.Store(swagger)
}
func ToProtoBinary(json []byte) ([]byte, error) {
document, err := openapi_v2.ParseDocument(json)
if err != nil {
return nil, err
}
return proto.Marshal(document)
}
// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
//
// Deprecated: use OpenAPIService.RegisterOpenAPIVersionedService instead.
func RegisterOpenAPIVersionedService(spec *spec.Swagger, servePath string, handler common.PathHandler) *OpenAPIService {
o := NewOpenAPIService(spec)
o.RegisterOpenAPIVersionedService(servePath, handler)
return o
}
// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handler common.PathHandler) {
accepted := []struct {
Type string
SubType string
ReturnedContentType string
GetDataAndEtag cached.Value[timedSpec]
}{
{"application", subTypeJSON, "application/" + subTypeJSON, o.jsonCache},
{"application", subTypeProtobufDeprecated, "application/" + subTypeProtobuf, o.protoCache},
{"application", subTypeProtobuf, "application/" + subTypeProtobuf, o.protoCache},
}
handler.Handle(servePath, gziphandler.GzipHandler(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
decipherableFormats := r.Header.Get("Accept")
if decipherableFormats == "" {
decipherableFormats = "*/*"
}
clauses := goautoneg.ParseAccept(decipherableFormats)
w.Header().Add("Vary", "Accept")
for _, clause := range clauses {
for _, accepts := range accepted {
if clause.Type != accepts.Type && clause.Type != "*" {
continue
}
if clause.SubType != accepts.SubType && clause.SubType != "*" {
continue
}
// serve the first matching media type in the sorted clause list
ts, etag, err := accepts.GetDataAndEtag.Get()
if err != nil {
klog.Errorf("Error in OpenAPI handler: %s", err)
// only return a 503 if we have no older cache data to serve
if ts.spec == nil {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
}
// Set Content-Type header in the reponse
w.Header().Set("Content-Type", accepts.ReturnedContentType)
// ETag must be enclosed in double quotes: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
w.Header().Set("Etag", strconv.Quote(etag))
// ServeContent will take care of caching using eTag.
http.ServeContent(w, r, servePath, ts.lastModified, bytes.NewReader(ts.spec))
return
}
}
// Return 406 for not acceptable format
w.WriteHeader(406)
return
}),
))
}
// BuildAndRegisterOpenAPIVersionedService builds the spec and registers a handler to provide access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIVersionedService.
//
// Deprecated: BuildAndRegisterOpenAPIVersionedServiceFromRoutes should be used instead.
func BuildAndRegisterOpenAPIVersionedService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
return BuildAndRegisterOpenAPIVersionedServiceFromRoutes(servePath, restfuladapter.AdaptWebServices(webServices), config, handler)
}
// BuildAndRegisterOpenAPIVersionedServiceFromRoutes builds the spec and registers a handler to provide access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIVersionedService.
func BuildAndRegisterOpenAPIVersionedServiceFromRoutes(servePath string, routeContainers []common.RouteContainer, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
spec, err := builder.BuildOpenAPISpecFromRoutes(routeContainers, config)
if err != nil {
return nil, err
}
o := NewOpenAPIService(spec)
o.RegisterOpenAPIVersionedService(servePath, handler)
return o, nil
}

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Alex Saskevich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,26 +0,0 @@
package govalidator
import "regexp"
// Basic regular expressions for validating strings
const (
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$"
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$"
ISBN13 string = "^(?:[0-9]{13})$"
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
)
var (
rxCreditCard = regexp.MustCompile(CreditCard)
rxInt = regexp.MustCompile(Int)
rxISBN10 = regexp.MustCompile(ISBN10)
rxISBN13 = regexp.MustCompile(ISBN13)
rxHexcolor = regexp.MustCompile(Hexcolor)
rxRGBcolor = regexp.MustCompile(RGBcolor)
rxBase64 = regexp.MustCompile(Base64)
rxSSN = regexp.MustCompile(SSN)
)

View File

@ -1,181 +0,0 @@
// Package govalidator is package of validators and sanitizers for strings, structs and collections.
package govalidator
import (
"fmt"
"net"
"net/url"
"reflect"
"regexp"
"strconv"
"strings"
)
var (
notNumberRegexp = regexp.MustCompile("[^0-9]+")
whiteSpacesAndMinus = regexp.MustCompile(`[\s-]+`)
)
// IsRequestURI check if the string rawurl, assuming
// it was received in an HTTP request, is an
// absolute URI or an absolute path.
func IsRequestURI(rawurl string) bool {
_, err := url.ParseRequestURI(rawurl)
return err == nil
}
// IsHexcolor check if the string is a hexadecimal color.
func IsHexcolor(str string) bool {
return rxHexcolor.MatchString(str)
}
// IsRGBcolor check if the string is a valid RGB color in form rgb(RRR, GGG, BBB).
func IsRGBcolor(str string) bool {
return rxRGBcolor.MatchString(str)
}
// IsCreditCard check if the string is a credit card.
func IsCreditCard(str string) bool {
sanitized := notNumberRegexp.ReplaceAllString(str, "")
if !rxCreditCard.MatchString(sanitized) {
return false
}
var sum int64
var digit string
var tmpNum int64
var shouldDouble bool
for i := len(sanitized) - 1; i >= 0; i-- {
digit = sanitized[i:(i + 1)]
tmpNum, _ = ToInt(digit)
if shouldDouble {
tmpNum *= 2
if tmpNum >= 10 {
sum += (tmpNum % 10) + 1
} else {
sum += tmpNum
}
} else {
sum += tmpNum
}
shouldDouble = !shouldDouble
}
return sum%10 == 0
}
// IsISBN10 check if the string is an ISBN version 10.
func IsISBN10(str string) bool {
return IsISBN(str, 10)
}
// IsISBN13 check if the string is an ISBN version 13.
func IsISBN13(str string) bool {
return IsISBN(str, 13)
}
// IsISBN check if the string is an ISBN (version 10 or 13).
// If version value is not equal to 10 or 13, it will be check both variants.
func IsISBN(str string, version int) bool {
sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "")
var checksum int32
var i int32
if version == 10 {
if !rxISBN10.MatchString(sanitized) {
return false
}
for i = 0; i < 9; i++ {
checksum += (i + 1) * int32(sanitized[i]-'0')
}
if sanitized[9] == 'X' {
checksum += 10 * 10
} else {
checksum += 10 * int32(sanitized[9]-'0')
}
if checksum%11 == 0 {
return true
}
return false
} else if version == 13 {
if !rxISBN13.MatchString(sanitized) {
return false
}
factor := []int32{1, 3}
for i = 0; i < 12; i++ {
checksum += factor[i%2] * int32(sanitized[i]-'0')
}
return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0
}
return IsISBN(str, 10) || IsISBN(str, 13)
}
// IsBase64 check if a string is base64 encoded.
func IsBase64(str string) bool {
return rxBase64.MatchString(str)
}
// IsIPv6 check if the string is an IP version 6.
func IsIPv6(str string) bool {
ip := net.ParseIP(str)
return ip != nil && strings.Contains(str, ":")
}
// IsMAC check if a string is valid MAC address.
// Possible MAC formats:
// 01:23:45:67:89:ab
// 01:23:45:67:89:ab:cd:ef
// 01-23-45-67-89-ab
// 01-23-45-67-89-ab-cd-ef
// 0123.4567.89ab
// 0123.4567.89ab.cdef
func IsMAC(str string) bool {
_, err := net.ParseMAC(str)
return err == nil
}
// IsSSN will validate the given string as a U.S. Social Security Number
func IsSSN(str string) bool {
if str == "" || len(str) != 11 {
return false
}
return rxSSN.MatchString(str)
}
// ToInt convert the input string or any int type to an integer type 64, or 0 if the input is not an integer.
func ToInt(value interface{}) (res int64, err error) {
val := reflect.ValueOf(value)
switch value.(type) {
case int, int8, int16, int32, int64:
res = val.Int()
case uint, uint8, uint16, uint32, uint64:
res = int64(val.Uint())
case string:
if IsInt(val.String()) {
res, err = strconv.ParseInt(val.String(), 0, 64)
if err != nil {
res = 0
}
} else {
err = fmt.Errorf("math: square root of negative number %g", value)
res = 0
}
default:
err = fmt.Errorf("math: square root of negative number %g", value)
res = 0
}
return
}
// IsInt check if the string is an integer. Empty string is valid.
func IsInt(str string) bool {
if IsNull(str) {
return true
}
return rxInt.MatchString(str)
}
// IsNull check if the string is null.
func IsNull(str string) bool {
return len(str) == 0
}

View File

@ -1,519 +0,0 @@
/*
Copyright 2017 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 schemamutation
import (
"k8s.io/kube-openapi/pkg/validation/spec"
)
// Walker runs callback functions on all references of an OpenAPI spec,
// replacing the values when visiting corresponding types.
type Walker struct {
// SchemaCallback will be called on each schema, taking the original schema,
// and before any other callbacks of the Walker.
// If the schema needs to be mutated, DO NOT mutate it in-place,
// always create a copy, mutate, and return it.
SchemaCallback func(schema *spec.Schema) *spec.Schema
// RefCallback will be called on each ref.
// If the ref needs to be mutated, DO NOT mutate it in-place,
// always create a copy, mutate, and return it.
RefCallback func(ref *spec.Ref) *spec.Ref
}
type SchemaCallbackFunc func(schema *spec.Schema) *spec.Schema
type RefCallbackFunc func(ref *spec.Ref) *spec.Ref
var SchemaCallBackNoop SchemaCallbackFunc = func(schema *spec.Schema) *spec.Schema {
return schema
}
var RefCallbackNoop RefCallbackFunc = func(ref *spec.Ref) *spec.Ref {
return ref
}
// ReplaceReferences rewrites the references without mutating the input.
// The output might share data with the input.
func ReplaceReferences(walkRef func(ref *spec.Ref) *spec.Ref, sp *spec.Swagger) *spec.Swagger {
walker := &Walker{RefCallback: walkRef, SchemaCallback: SchemaCallBackNoop}
return walker.WalkRoot(sp)
}
func (w *Walker) WalkSchema(schema *spec.Schema) *spec.Schema {
if schema == nil {
return nil
}
orig := schema
clone := func() {
if orig == schema {
schema = &spec.Schema{}
*schema = *orig
}
}
// Always run callback on the whole schema first
// so that SchemaCallback can take the original schema as input.
schema = w.SchemaCallback(schema)
if r := w.RefCallback(&schema.Ref); r != &schema.Ref {
clone()
schema.Ref = *r
}
definitionsCloned := false
for k, v := range schema.Definitions {
if s := w.WalkSchema(&v); s != &v {
if !definitionsCloned {
definitionsCloned = true
clone()
schema.Definitions = make(spec.Definitions, len(orig.Definitions))
for k2, v2 := range orig.Definitions {
schema.Definitions[k2] = v2
}
}
schema.Definitions[k] = *s
}
}
propertiesCloned := false
for k, v := range schema.Properties {
if s := w.WalkSchema(&v); s != &v {
if !propertiesCloned {
propertiesCloned = true
clone()
schema.Properties = make(map[string]spec.Schema, len(orig.Properties))
for k2, v2 := range orig.Properties {
schema.Properties[k2] = v2
}
}
schema.Properties[k] = *s
}
}
patternPropertiesCloned := false
for k, v := range schema.PatternProperties {
if s := w.WalkSchema(&v); s != &v {
if !patternPropertiesCloned {
patternPropertiesCloned = true
clone()
schema.PatternProperties = make(map[string]spec.Schema, len(orig.PatternProperties))
for k2, v2 := range orig.PatternProperties {
schema.PatternProperties[k2] = v2
}
}
schema.PatternProperties[k] = *s
}
}
allOfCloned := false
for i := range schema.AllOf {
if s := w.WalkSchema(&schema.AllOf[i]); s != &schema.AllOf[i] {
if !allOfCloned {
allOfCloned = true
clone()
schema.AllOf = make([]spec.Schema, len(orig.AllOf))
copy(schema.AllOf, orig.AllOf)
}
schema.AllOf[i] = *s
}
}
anyOfCloned := false
for i := range schema.AnyOf {
if s := w.WalkSchema(&schema.AnyOf[i]); s != &schema.AnyOf[i] {
if !anyOfCloned {
anyOfCloned = true
clone()
schema.AnyOf = make([]spec.Schema, len(orig.AnyOf))
copy(schema.AnyOf, orig.AnyOf)
}
schema.AnyOf[i] = *s
}
}
oneOfCloned := false
for i := range schema.OneOf {
if s := w.WalkSchema(&schema.OneOf[i]); s != &schema.OneOf[i] {
if !oneOfCloned {
oneOfCloned = true
clone()
schema.OneOf = make([]spec.Schema, len(orig.OneOf))
copy(schema.OneOf, orig.OneOf)
}
schema.OneOf[i] = *s
}
}
if schema.Not != nil {
if s := w.WalkSchema(schema.Not); s != schema.Not {
clone()
schema.Not = s
}
}
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
if s := w.WalkSchema(schema.AdditionalProperties.Schema); s != schema.AdditionalProperties.Schema {
clone()
schema.AdditionalProperties = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalProperties.Allows}
}
}
if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil {
if s := w.WalkSchema(schema.AdditionalItems.Schema); s != schema.AdditionalItems.Schema {
clone()
schema.AdditionalItems = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalItems.Allows}
}
}
if schema.Items != nil {
if schema.Items.Schema != nil {
if s := w.WalkSchema(schema.Items.Schema); s != schema.Items.Schema {
clone()
schema.Items = &spec.SchemaOrArray{Schema: s}
}
} else {
itemsCloned := false
for i := range schema.Items.Schemas {
if s := w.WalkSchema(&schema.Items.Schemas[i]); s != &schema.Items.Schemas[i] {
if !itemsCloned {
clone()
schema.Items = &spec.SchemaOrArray{
Schemas: make([]spec.Schema, len(orig.Items.Schemas)),
}
itemsCloned = true
copy(schema.Items.Schemas, orig.Items.Schemas)
}
schema.Items.Schemas[i] = *s
}
}
}
}
return schema
}
func (w *Walker) walkParameter(param *spec.Parameter) *spec.Parameter {
if param == nil {
return nil
}
orig := param
cloned := false
clone := func() {
if !cloned {
cloned = true
param = &spec.Parameter{}
*param = *orig
}
}
if r := w.RefCallback(&param.Ref); r != &param.Ref {
clone()
param.Ref = *r
}
if s := w.WalkSchema(param.Schema); s != param.Schema {
clone()
param.Schema = s
}
if param.Items != nil {
if r := w.RefCallback(&param.Items.Ref); r != &param.Items.Ref {
param.Items.Ref = *r
}
}
return param
}
func (w *Walker) walkParameters(params []spec.Parameter) ([]spec.Parameter, bool) {
if params == nil {
return nil, false
}
orig := params
cloned := false
clone := func() {
if !cloned {
cloned = true
params = make([]spec.Parameter, len(params))
copy(params, orig)
}
}
for i := range params {
if s := w.walkParameter(&params[i]); s != &params[i] {
clone()
params[i] = *s
}
}
return params, cloned
}
func (w *Walker) walkResponse(resp *spec.Response) *spec.Response {
if resp == nil {
return nil
}
orig := resp
cloned := false
clone := func() {
if !cloned {
cloned = true
resp = &spec.Response{}
*resp = *orig
}
}
if r := w.RefCallback(&resp.Ref); r != &resp.Ref {
clone()
resp.Ref = *r
}
if s := w.WalkSchema(resp.Schema); s != resp.Schema {
clone()
resp.Schema = s
}
return resp
}
func (w *Walker) walkResponses(resps *spec.Responses) *spec.Responses {
if resps == nil {
return nil
}
orig := resps
cloned := false
clone := func() {
if !cloned {
cloned = true
resps = &spec.Responses{}
*resps = *orig
}
}
if r := w.walkResponse(resps.ResponsesProps.Default); r != resps.ResponsesProps.Default {
clone()
resps.Default = r
}
responsesCloned := false
for k, v := range resps.ResponsesProps.StatusCodeResponses {
if r := w.walkResponse(&v); r != &v {
if !responsesCloned {
responsesCloned = true
clone()
resps.ResponsesProps.StatusCodeResponses = make(map[int]spec.Response, len(orig.StatusCodeResponses))
for k2, v2 := range orig.StatusCodeResponses {
resps.ResponsesProps.StatusCodeResponses[k2] = v2
}
}
resps.ResponsesProps.StatusCodeResponses[k] = *r
}
}
return resps
}
func (w *Walker) walkOperation(op *spec.Operation) *spec.Operation {
if op == nil {
return nil
}
orig := op
cloned := false
clone := func() {
if !cloned {
cloned = true
op = &spec.Operation{}
*op = *orig
}
}
parametersCloned := false
for i := range op.Parameters {
if s := w.walkParameter(&op.Parameters[i]); s != &op.Parameters[i] {
if !parametersCloned {
parametersCloned = true
clone()
op.Parameters = make([]spec.Parameter, len(orig.Parameters))
copy(op.Parameters, orig.Parameters)
}
op.Parameters[i] = *s
}
}
if r := w.walkResponses(op.Responses); r != op.Responses {
clone()
op.Responses = r
}
return op
}
func (w *Walker) walkPathItem(pathItem *spec.PathItem) *spec.PathItem {
if pathItem == nil {
return nil
}
orig := pathItem
cloned := false
clone := func() {
if !cloned {
cloned = true
pathItem = &spec.PathItem{}
*pathItem = *orig
}
}
if p, changed := w.walkParameters(pathItem.Parameters); changed {
clone()
pathItem.Parameters = p
}
if op := w.walkOperation(pathItem.Get); op != pathItem.Get {
clone()
pathItem.Get = op
}
if op := w.walkOperation(pathItem.Head); op != pathItem.Head {
clone()
pathItem.Head = op
}
if op := w.walkOperation(pathItem.Delete); op != pathItem.Delete {
clone()
pathItem.Delete = op
}
if op := w.walkOperation(pathItem.Options); op != pathItem.Options {
clone()
pathItem.Options = op
}
if op := w.walkOperation(pathItem.Patch); op != pathItem.Patch {
clone()
pathItem.Patch = op
}
if op := w.walkOperation(pathItem.Post); op != pathItem.Post {
clone()
pathItem.Post = op
}
if op := w.walkOperation(pathItem.Put); op != pathItem.Put {
clone()
pathItem.Put = op
}
return pathItem
}
func (w *Walker) walkPaths(paths *spec.Paths) *spec.Paths {
if paths == nil {
return nil
}
orig := paths
cloned := false
clone := func() {
if !cloned {
cloned = true
paths = &spec.Paths{}
*paths = *orig
}
}
pathsCloned := false
for k, v := range paths.Paths {
if p := w.walkPathItem(&v); p != &v {
if !pathsCloned {
pathsCloned = true
clone()
paths.Paths = make(map[string]spec.PathItem, len(orig.Paths))
for k2, v2 := range orig.Paths {
paths.Paths[k2] = v2
}
}
paths.Paths[k] = *p
}
}
return paths
}
func (w *Walker) WalkRoot(swagger *spec.Swagger) *spec.Swagger {
if swagger == nil {
return nil
}
orig := swagger
cloned := false
clone := func() {
if !cloned {
cloned = true
swagger = &spec.Swagger{}
*swagger = *orig
}
}
parametersCloned := false
for k, v := range swagger.Parameters {
if p := w.walkParameter(&v); p != &v {
if !parametersCloned {
parametersCloned = true
clone()
swagger.Parameters = make(map[string]spec.Parameter, len(orig.Parameters))
for k2, v2 := range orig.Parameters {
swagger.Parameters[k2] = v2
}
}
swagger.Parameters[k] = *p
}
}
responsesCloned := false
for k, v := range swagger.Responses {
if r := w.walkResponse(&v); r != &v {
if !responsesCloned {
responsesCloned = true
clone()
swagger.Responses = make(map[string]spec.Response, len(orig.Responses))
for k2, v2 := range orig.Responses {
swagger.Responses[k2] = v2
}
}
swagger.Responses[k] = *r
}
}
definitionsCloned := false
for k, v := range swagger.Definitions {
if s := w.WalkSchema(&v); s != &v {
if !definitionsCloned {
definitionsCloned = true
clone()
swagger.Definitions = make(spec.Definitions, len(orig.Definitions))
for k2, v2 := range orig.Definitions {
swagger.Definitions[k2] = v2
}
}
swagger.Definitions[k] = *s
}
}
if swagger.Paths != nil {
if p := w.walkPaths(swagger.Paths); p != swagger.Paths {
clone()
swagger.Paths = p
}
}
return swagger
}

View File

@ -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 util
// A simple trie implementation with Add and HasPrefix methods only.
type Trie struct {
children map[byte]*Trie
wordTail bool
word string
}
// NewTrie creates a Trie and add all strings in the provided list to it.
func NewTrie(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
}

View File

@ -1,115 +0,0 @@
/*
Copyright 2017 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 util
import (
"reflect"
"strings"
)
// [DEPRECATED] ToCanonicalName converts Golang package/type canonical name into REST friendly OpenAPI name.
// This method is deprecated because it has a misleading name. Please use ToRESTFriendlyName
// instead
//
// NOTE: actually the "canonical name" in this method should be named "REST friendly OpenAPI name",
// which is different from "canonical name" defined in GetCanonicalTypeName. The "canonical name" defined
// in GetCanonicalTypeName means Go type names with full package path.
//
// Examples of REST friendly OpenAPI name:
//
// Input: k8s.io/api/core/v1.Pod
// Output: io.k8s.api.core.v1.Pod
//
// Input: k8s.io/api/core/v1
// Output: io.k8s.api.core.v1
//
// Input: csi.storage.k8s.io/v1alpha1.CSINodeInfo
// Output: io.k8s.storage.csi.v1alpha1.CSINodeInfo
func ToCanonicalName(name string) string {
return ToRESTFriendlyName(name)
}
// ToRESTFriendlyName converts Golang package/type canonical name into REST friendly OpenAPI name.
//
// Examples of REST friendly OpenAPI name:
//
// Input: k8s.io/api/core/v1.Pod
// Output: io.k8s.api.core.v1.Pod
//
// Input: k8s.io/api/core/v1
// Output: io.k8s.api.core.v1
//
// Input: csi.storage.k8s.io/v1alpha1.CSINodeInfo
// Output: io.k8s.storage.csi.v1alpha1.CSINodeInfo
func ToRESTFriendlyName(name string) string {
nameParts := strings.Split(name, "/")
// Reverse first part. e.g., io.k8s... instead of k8s.io...
if len(nameParts) > 0 && strings.Contains(nameParts[0], ".") {
parts := strings.Split(nameParts[0], ".")
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
parts[i], parts[j] = parts[j], parts[i]
}
nameParts[0] = strings.Join(parts, ".")
}
return strings.Join(nameParts, ".")
}
// OpenAPICanonicalTypeNamer is an interface for models without Go type to seed model name.
//
// OpenAPI canonical names are Go type names with full package path, for uniquely indentifying
// a model / Go type. If a Go type is vendored from another package, only the path after "/vendor/"
// should be used. For custom resource definition (CRD), the canonical name is expected to be
//
// group/version.kind
//
// Examples of canonical name:
//
// Go type: k8s.io/kubernetes/pkg/apis/core.Pod
// CRD: csi.storage.k8s.io/v1alpha1.CSINodeInfo
//
// Example for vendored Go type:
//
// Original full path: k8s.io/kubernetes/vendor/k8s.io/api/core/v1.Pod
// Canonical name: k8s.io/api/core/v1.Pod
//
// Original full path: vendor/k8s.io/api/core/v1.Pod
// Canonical name: k8s.io/api/core/v1.Pod
type OpenAPICanonicalTypeNamer interface {
OpenAPICanonicalTypeName() string
}
// GetCanonicalTypeName will find the canonical type name of a sample object, removing
// the "vendor" part of the path
func GetCanonicalTypeName(model interface{}) string {
if namer, ok := model.(OpenAPICanonicalTypeNamer); ok {
return namer.OpenAPICanonicalTypeName()
}
t := reflect.TypeOf(model)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.PkgPath() == "" {
return t.Name()
}
path := t.PkgPath()
if strings.Contains(path, "/vendor/") {
path = path[strings.Index(path, "/vendor/")+len("/vendor/"):]
} else if strings.HasPrefix(path, "vendor/") {
path = strings.TrimPrefix(path, "vendor/")
}
return path + "." + t.Name()
}

View File

@ -1,2 +0,0 @@
secrets.yml
coverage.out

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -1,46 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 errors
import (
"fmt"
)
// Error represents a error interface all swagger framework errors implement
type Error interface {
error
Code() int32
}
type apiError struct {
code int32
message string
}
func (a *apiError) Error() string {
return a.message
}
func (a *apiError) Code() int32 {
return a.code
}
// New creates a new API error with a code and a message
func New(code int32, message string, args ...interface{}) Error {
if len(args) > 0 {
return &apiError{code, fmt.Sprintf(message, args...)}
}
return &apiError{code, message}
}

View File

@ -1,26 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 errors provides an Error interface and several concrete types
implementing this interface to manage API errors and JSON-schema validation
errors.
A middleware handler ServeError() is provided to serve the errors types
it defines.
It is used throughout the various go-openapi toolkit libraries
(https://github.com/go-openapi).
*/
package errors

View File

@ -1,44 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 errors
// Validation represents a failure of a precondition
type Validation struct {
code int32
Name string
In string
Value interface{}
Valid interface{}
message string
Values []interface{}
}
func (e *Validation) Error() string {
return e.message
}
// Code the error code
func (e *Validation) Code() int32 {
return e.code
}
// ValidateName produces an error message name for an aliased property
func (e *Validation) ValidateName(name string) *Validation {
if e.Name == "" && name != "" {
e.Name = name
e.message = name + e.message
}
return e
}

View File

@ -1,573 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 errors
import (
"fmt"
"strings"
)
const (
invalidType = "%s is an invalid type name"
typeFail = "%s in %s must be of type %s"
typeFailWithData = "%s in %s must be of type %s: %q"
typeFailWithError = "%s in %s must be of type %s, because: %s"
requiredFail = "%s in %s is required"
tooLongMessage = "%s in %s should be at most %d chars long"
tooShortMessage = "%s in %s should be at least %d chars long"
patternFail = "%s in %s should match '%s'"
enumFail = "%s in %s should be one of %v"
multipleOfFail = "%s in %s should be a multiple of %v"
maxIncFail = "%s in %s should be less than or equal to %v"
maxExcFail = "%s in %s should be less than %v"
minIncFail = "%s in %s should be greater than or equal to %v"
minExcFail = "%s in %s should be greater than %v"
uniqueFail = "%s in %s shouldn't contain duplicates"
maxItemsFail = "%s in %s should have at most %d items"
minItemsFail = "%s in %s should have at least %d items"
typeFailNoIn = "%s must be of type %s"
typeFailWithDataNoIn = "%s must be of type %s: %q"
typeFailWithErrorNoIn = "%s must be of type %s, because: %s"
requiredFailNoIn = "%s is required"
tooLongMessageNoIn = "%s should be at most %d chars long"
tooShortMessageNoIn = "%s should be at least %d chars long"
patternFailNoIn = "%s should match '%s'"
enumFailNoIn = "%s should be one of %v"
multipleOfFailNoIn = "%s should be a multiple of %v"
maxIncFailNoIn = "%s should be less than or equal to %v"
maxExcFailNoIn = "%s should be less than %v"
minIncFailNoIn = "%s should be greater than or equal to %v"
minExcFailNoIn = "%s should be greater than %v"
uniqueFailNoIn = "%s shouldn't contain duplicates"
maxItemsFailNoIn = "%s should have at most %d items"
minItemsFailNoIn = "%s should have at least %d items"
noAdditionalItems = "%s in %s can't have additional items"
noAdditionalItemsNoIn = "%s can't have additional items"
tooFewProperties = "%s in %s should have at least %d properties"
tooFewPropertiesNoIn = "%s should have at least %d properties"
tooManyProperties = "%s in %s should have at most %d properties"
tooManyPropertiesNoIn = "%s should have at most %d properties"
unallowedProperty = "%s.%s in %s is a forbidden property"
unallowedPropertyNoIn = "%s.%s is a forbidden property"
failedAllPatternProps = "%s.%s in %s failed all pattern properties"
failedAllPatternPropsNoIn = "%s.%s failed all pattern properties"
multipleOfMustBePositive = "factor MultipleOf declared for %s must be positive: %v"
)
// All code responses can be used to differentiate errors for different handling
// by the consuming program
const (
// CompositeErrorCode remains 422 for backwards-compatibility
// and to separate it from validation errors with cause
CompositeErrorCode = 422
// InvalidTypeCode is used for any subclass of invalid types
InvalidTypeCode = 600 + iota
RequiredFailCode
TooLongFailCode
TooShortFailCode
PatternFailCode
EnumFailCode
MultipleOfFailCode
MaxFailCode
MinFailCode
UniqueFailCode
MaxItemsFailCode
MinItemsFailCode
NoAdditionalItemsCode
TooFewPropertiesCode
TooManyPropertiesCode
UnallowedPropertyCode
FailedAllPatternPropsCode
MultipleOfMustBePositiveCode
)
// CompositeError is an error that groups several errors together
type CompositeError struct {
Errors []error
code int32
message string
}
// Code for this error
func (c *CompositeError) Code() int32 {
return c.code
}
func (c *CompositeError) Error() string {
if len(c.Errors) > 0 {
msgs := []string{c.message + ":"}
for _, e := range c.Errors {
msgs = append(msgs, e.Error())
}
return strings.Join(msgs, "\n")
}
return c.message
}
// CompositeValidationError an error to wrap a bunch of other errors
func CompositeValidationError(errors ...error) *CompositeError {
return &CompositeError{
code: CompositeErrorCode,
Errors: append([]error{}, errors...),
message: "validation failure list",
}
}
// FailedAllPatternProperties an error for when the property doesn't match a pattern
func FailedAllPatternProperties(name, in, key string) *Validation {
msg := fmt.Sprintf(failedAllPatternProps, name, key, in)
if in == "" {
msg = fmt.Sprintf(failedAllPatternPropsNoIn, name, key)
}
return &Validation{
code: FailedAllPatternPropsCode,
Name: name,
In: in,
Value: key,
message: msg,
}
}
// PropertyNotAllowed an error for when the property doesn't match a pattern
func PropertyNotAllowed(name, in, key string) *Validation {
msg := fmt.Sprintf(unallowedProperty, name, key, in)
if in == "" {
msg = fmt.Sprintf(unallowedPropertyNoIn, name, key)
}
return &Validation{
code: UnallowedPropertyCode,
Name: name,
In: in,
Value: key,
message: msg,
}
}
// TooFewProperties an error for an object with too few properties
func TooFewProperties(name, in string, minProperties, size int64) *Validation {
msg := fmt.Sprintf(tooFewProperties, name, in, minProperties)
if in == "" {
msg = fmt.Sprintf(tooFewPropertiesNoIn, name, minProperties)
}
return &Validation{
code: TooFewPropertiesCode,
Name: name,
In: in,
Value: size,
Valid: minProperties,
message: msg,
}
}
// TooManyProperties an error for an object with too many properties
func TooManyProperties(name, in string, maxProperties, size int64) *Validation {
msg := fmt.Sprintf(tooManyProperties, name, in, maxProperties)
if in == "" {
msg = fmt.Sprintf(tooManyPropertiesNoIn, name, maxProperties)
}
return &Validation{
code: TooManyPropertiesCode,
Name: name,
In: in,
Value: size,
Valid: maxProperties,
message: msg,
}
}
// AdditionalItemsNotAllowed an error for invalid additional items
func AdditionalItemsNotAllowed(name, in string) *Validation {
msg := fmt.Sprintf(noAdditionalItems, name, in)
if in == "" {
msg = fmt.Sprintf(noAdditionalItemsNoIn, name)
}
return &Validation{
code: NoAdditionalItemsCode,
Name: name,
In: in,
message: msg,
}
}
// InvalidCollectionFormat another flavor of invalid type error
func InvalidCollectionFormat(name, in, format string) *Validation {
return &Validation{
code: InvalidTypeCode,
Name: name,
In: in,
Value: format,
message: fmt.Sprintf("the collection format %q is not supported for the %s param %q", format, in, name),
}
}
// InvalidTypeName an error for when the type is invalid
func InvalidTypeName(typeName string) *Validation {
return &Validation{
code: InvalidTypeCode,
Value: typeName,
message: fmt.Sprintf(invalidType, typeName),
}
}
// InvalidType creates an error for when the type is invalid
func InvalidType(name, in, typeName string, value interface{}) *Validation {
var message string
if in != "" {
switch value.(type) {
case string:
message = fmt.Sprintf(typeFailWithData, name, in, typeName, value)
case error:
message = fmt.Sprintf(typeFailWithError, name, in, typeName, value)
default:
message = fmt.Sprintf(typeFail, name, in, typeName)
}
} else {
switch value.(type) {
case string:
message = fmt.Sprintf(typeFailWithDataNoIn, name, typeName, value)
case error:
message = fmt.Sprintf(typeFailWithErrorNoIn, name, typeName, value)
default:
message = fmt.Sprintf(typeFailNoIn, name, typeName)
}
}
return &Validation{
code: InvalidTypeCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// DuplicateItems error for when an array contains duplicates
func DuplicateItems(name, in string) *Validation {
msg := fmt.Sprintf(uniqueFail, name, in)
if in == "" {
msg = fmt.Sprintf(uniqueFailNoIn, name)
}
return &Validation{
code: UniqueFailCode,
Name: name,
In: in,
message: msg,
}
}
// TooManyItems error for when an array contains too many items
func TooManyItems(name, in string, max int64, value interface{}) *Validation {
msg := fmt.Sprintf(maxItemsFail, name, in, max)
if in == "" {
msg = fmt.Sprintf(maxItemsFailNoIn, name, max)
}
return &Validation{
code: MaxItemsFailCode,
Name: name,
In: in,
Value: value,
Valid: max,
message: msg,
}
}
// TooFewItems error for when an array contains too few items
func TooFewItems(name, in string, min int64, value interface{}) *Validation {
msg := fmt.Sprintf(minItemsFail, name, in, min)
if in == "" {
msg = fmt.Sprintf(minItemsFailNoIn, name, min)
}
return &Validation{
code: MinItemsFailCode,
Name: name,
In: in,
Value: value,
Valid: min,
message: msg,
}
}
// ExceedsMaximumInt error for when maxinum validation fails
func ExceedsMaximumInt(name, in string, max int64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := maxIncFailNoIn
if exclusive {
m = maxExcFailNoIn
}
message = fmt.Sprintf(m, name, max)
} else {
m := maxIncFail
if exclusive {
m = maxExcFail
}
message = fmt.Sprintf(m, name, in, max)
}
return &Validation{
code: MaxFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMaximumUint error for when maxinum validation fails
func ExceedsMaximumUint(name, in string, max uint64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := maxIncFailNoIn
if exclusive {
m = maxExcFailNoIn
}
message = fmt.Sprintf(m, name, max)
} else {
m := maxIncFail
if exclusive {
m = maxExcFail
}
message = fmt.Sprintf(m, name, in, max)
}
return &Validation{
code: MaxFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMaximum error for when maxinum validation fails
func ExceedsMaximum(name, in string, max float64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := maxIncFailNoIn
if exclusive {
m = maxExcFailNoIn
}
message = fmt.Sprintf(m, name, max)
} else {
m := maxIncFail
if exclusive {
m = maxExcFail
}
message = fmt.Sprintf(m, name, in, max)
}
return &Validation{
code: MaxFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMinimumInt error for when maxinum validation fails
func ExceedsMinimumInt(name, in string, min int64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := minIncFailNoIn
if exclusive {
m = minExcFailNoIn
}
message = fmt.Sprintf(m, name, min)
} else {
m := minIncFail
if exclusive {
m = minExcFail
}
message = fmt.Sprintf(m, name, in, min)
}
return &Validation{
code: MinFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMinimumUint error for when maxinum validation fails
func ExceedsMinimumUint(name, in string, min uint64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := minIncFailNoIn
if exclusive {
m = minExcFailNoIn
}
message = fmt.Sprintf(m, name, min)
} else {
m := minIncFail
if exclusive {
m = minExcFail
}
message = fmt.Sprintf(m, name, in, min)
}
return &Validation{
code: MinFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// ExceedsMinimum error for when maxinum validation fails
func ExceedsMinimum(name, in string, min float64, exclusive bool, value interface{}) *Validation {
var message string
if in == "" {
m := minIncFailNoIn
if exclusive {
m = minExcFailNoIn
}
message = fmt.Sprintf(m, name, min)
} else {
m := minIncFail
if exclusive {
m = minExcFail
}
message = fmt.Sprintf(m, name, in, min)
}
return &Validation{
code: MinFailCode,
Name: name,
In: in,
Value: value,
message: message,
}
}
// NotMultipleOf error for when multiple of validation fails
func NotMultipleOf(name, in string, multiple, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(multipleOfFailNoIn, name, multiple)
} else {
msg = fmt.Sprintf(multipleOfFail, name, in, multiple)
}
return &Validation{
code: MultipleOfFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// EnumFail error for when an enum validation fails
func EnumFail(name, in string, value interface{}, values []interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(enumFailNoIn, name, values)
} else {
msg = fmt.Sprintf(enumFail, name, in, values)
}
return &Validation{
code: EnumFailCode,
Name: name,
In: in,
Value: value,
Values: values,
message: msg,
}
}
// Required error for when a value is missing
func Required(name, in string) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(requiredFailNoIn, name)
} else {
msg = fmt.Sprintf(requiredFail, name, in)
}
return &Validation{
code: RequiredFailCode,
Name: name,
In: in,
message: msg,
}
}
// TooLong error for when a string is too long
func TooLong(name, in string, max int64, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(tooLongMessageNoIn, name, max)
} else {
msg = fmt.Sprintf(tooLongMessage, name, in, max)
}
return &Validation{
code: TooLongFailCode,
Name: name,
In: in,
Value: value,
Valid: max,
message: msg,
}
}
// TooShort error for when a string is too short
func TooShort(name, in string, min int64, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(tooShortMessageNoIn, name, min)
} else {
msg = fmt.Sprintf(tooShortMessage, name, in, min)
}
return &Validation{
code: TooShortFailCode,
Name: name,
In: in,
Value: value,
Valid: min,
message: msg,
}
}
// FailedPattern error for when a string fails a regex pattern match
// the pattern that is returned is the ECMA syntax version of the pattern not the golang version.
func FailedPattern(name, in, pattern string, value interface{}) *Validation {
var msg string
if in == "" {
msg = fmt.Sprintf(patternFailNoIn, name, pattern)
} else {
msg = fmt.Sprintf(patternFail, name, in, pattern)
}
return &Validation{
code: PatternFailCode,
Name: name,
In: in,
Value: value,
message: msg,
}
}
// MultipleOfMustBePositive error for when a
// multipleOf factor is negative
func MultipleOfMustBePositive(name, in string, factor interface{}) *Validation {
return &Validation{
code: MultipleOfMustBePositiveCode,
Name: name,
In: in,
Value: factor,
message: fmt.Sprintf(multipleOfMustBePositive, name, factor),
}
}

View File

@ -1,2 +0,0 @@
secrets.yml
coverage.out

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -1,103 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 strfmt
import (
bsonprim "k8s.io/kube-openapi/pkg/validation/strfmt/bson"
)
func init() {
var id ObjectId
// register this format in the default registry
Default.Add("bsonobjectid", &id, IsBSONObjectID)
}
// IsBSONObjectID returns true when the string is a valid BSON.ObjectId
func IsBSONObjectID(str string) bool {
_, err := bsonprim.ObjectIDFromHex(str)
return err == nil
}
// ObjectId represents a BSON object ID (alias to go.mongodb.org/mongo-driver/bson/primitive.ObjectID)
//
// swagger:strfmt bsonobjectid
type ObjectId bsonprim.ObjectID
// NewObjectId creates a ObjectId from a Hex String
func NewObjectId(hex string) ObjectId {
oid, err := bsonprim.ObjectIDFromHex(hex)
if err != nil {
panic(err)
}
return ObjectId(oid)
}
// MarshalText turns this instance into text
func (id ObjectId) MarshalText() ([]byte, error) {
oid := bsonprim.ObjectID(id)
if oid == bsonprim.NilObjectID {
return nil, nil
}
return []byte(oid.Hex()), nil
}
// UnmarshalText hydrates this instance from text
func (id *ObjectId) UnmarshalText(data []byte) error { // validation is performed later on
if len(data) == 0 {
*id = ObjectId(bsonprim.NilObjectID)
return nil
}
oidstr := string(data)
oid, err := bsonprim.ObjectIDFromHex(oidstr)
if err != nil {
return err
}
*id = ObjectId(oid)
return nil
}
func (id ObjectId) String() string {
return bsonprim.ObjectID(id).String()
}
// MarshalJSON returns the ObjectId as JSON
func (id ObjectId) MarshalJSON() ([]byte, error) {
return bsonprim.ObjectID(id).MarshalJSON()
}
// UnmarshalJSON sets the ObjectId from JSON
func (id *ObjectId) UnmarshalJSON(data []byte) error {
var obj bsonprim.ObjectID
if err := obj.UnmarshalJSON(data); err != nil {
return err
}
*id = ObjectId(obj)
return nil
}
// DeepCopyInto copies the receiver and writes its value into out.
func (id *ObjectId) DeepCopyInto(out *ObjectId) {
*out = *id
}
// DeepCopy copies the receiver into a new ObjectId.
func (id *ObjectId) DeepCopy() *ObjectId {
if id == nil {
return nil
}
out := new(ObjectId)
id.DeepCopyInto(out)
return out
}

View File

@ -1,122 +0,0 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// 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
//
// Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer
// See THIRD-PARTY-NOTICES for original license terms.
package bson
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
)
// ErrInvalidHex indicates that a hex string cannot be converted to an ObjectID.
var ErrInvalidHex = errors.New("the provided hex string is not a valid ObjectID")
// ObjectID is the BSON ObjectID type.
type ObjectID [12]byte
// NilObjectID is the zero value for ObjectID.
var NilObjectID ObjectID
// Hex returns the hex encoding of the ObjectID as a string.
func (id ObjectID) Hex() string {
return hex.EncodeToString(id[:])
}
func (id ObjectID) String() string {
return fmt.Sprintf("ObjectID(%q)", id.Hex())
}
// IsZero returns true if id is the empty ObjectID.
func (id ObjectID) IsZero() bool {
return bytes.Equal(id[:], NilObjectID[:])
}
// ObjectIDFromHex creates a new ObjectID from a hex string. It returns an error if the hex string is not a
// valid ObjectID.
func ObjectIDFromHex(s string) (ObjectID, error) {
b, err := hex.DecodeString(s)
if err != nil {
return NilObjectID, err
}
if len(b) != 12 {
return NilObjectID, ErrInvalidHex
}
var oid [12]byte
copy(oid[:], b[:])
return oid, nil
}
// MarshalJSON returns the ObjectID as a string
func (id ObjectID) MarshalJSON() ([]byte, error) {
return json.Marshal(id.Hex())
}
// UnmarshalJSON populates the byte slice with the ObjectID. If the byte slice is 24 bytes long, it
// will be populated with the hex representation of the ObjectID. If the byte slice is twelve bytes
// long, it will be populated with the BSON representation of the ObjectID. This method also accepts empty strings and
// decodes them as NilObjectID. For any other inputs, an error will be returned.
func (id *ObjectID) UnmarshalJSON(b []byte) error {
// Ignore "null" to keep parity with the standard library. Decoding a JSON null into a non-pointer ObjectID field
// will leave the field unchanged. For pointer values, encoding/json will set the pointer to nil and will not
// enter the UnmarshalJSON hook.
if string(b) == "null" {
return nil
}
var err error
switch len(b) {
case 12:
copy(id[:], b)
default:
// Extended JSON
var res interface{}
err := json.Unmarshal(b, &res)
if err != nil {
return err
}
str, ok := res.(string)
if !ok {
m, ok := res.(map[string]interface{})
if !ok {
return errors.New("not an extended JSON ObjectID")
}
oid, ok := m["$oid"]
if !ok {
return errors.New("not an extended JSON ObjectID")
}
str, ok = oid.(string)
if !ok {
return errors.New("not an extended JSON ObjectID")
}
}
// An empty string is not a valid ObjectID, but we treat it as a special value that decodes as NilObjectID.
if len(str) == 0 {
copy(id[:], NilObjectID[:])
return nil
}
if len(str) != 24 {
return fmt.Errorf("cannot unmarshal into an ObjectID, the length must be 24 but it is %d", len(str))
}
_, err = hex.Decode(id[:], []byte(str))
if err != nil {
return err
}
}
return err
}

View File

@ -1,103 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 strfmt
import (
"encoding/json"
"time"
)
func init() {
d := Date{}
// register this format in the default registry
Default.Add("date", &d, IsDate)
}
// IsDate returns true when the string is a valid date
func IsDate(str string) bool {
_, err := time.Parse(RFC3339FullDate, str)
return err == nil
}
const (
// RFC3339FullDate represents a full-date as specified by RFC3339
// See: http://goo.gl/xXOvVd
RFC3339FullDate = "2006-01-02"
)
// Date represents a date from the API
//
// swagger:strfmt date
type Date time.Time
// String converts this date into a string
func (d Date) String() string {
return time.Time(d).Format(RFC3339FullDate)
}
// UnmarshalText parses a text representation into a date type
func (d *Date) UnmarshalText(text []byte) error {
if len(text) == 0 {
return nil
}
dd, err := time.Parse(RFC3339FullDate, string(text))
if err != nil {
return err
}
*d = Date(dd)
return nil
}
// MarshalText serializes this date type to string
func (d Date) MarshalText() ([]byte, error) {
return []byte(d.String()), nil
}
// MarshalJSON returns the Date as JSON
func (d Date) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(d).Format(RFC3339FullDate))
}
// UnmarshalJSON sets the Date from JSON
func (d *Date) UnmarshalJSON(data []byte) error {
if string(data) == jsonNull {
return nil
}
var strdate string
if err := json.Unmarshal(data, &strdate); err != nil {
return err
}
tt, err := time.Parse(RFC3339FullDate, strdate)
if err != nil {
return err
}
*d = Date(tt)
return nil
}
// DeepCopyInto copies the receiver and writes its value into out.
func (d *Date) DeepCopyInto(out *Date) {
*out = *d
}
// DeepCopy copies the receiver into a new Date.
func (d *Date) DeepCopy() *Date {
if d == nil {
return nil
}
out := new(Date)
d.DeepCopyInto(out)
return out
}

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 strfmt contains custom string formats
//
// TODO: add info on how to define and register a custom format
package strfmt

View File

@ -1,180 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 strfmt
import (
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"
"time"
)
func init() {
d := Duration(0)
// register this format in the default registry
Default.Add("duration", &d, IsDuration)
}
var (
timeUnits = [][]string{
{"ns", "nano"},
{"us", "µs", "micro"},
{"ms", "milli"},
{"s", "sec"},
{"m", "min"},
{"h", "hr", "hour"},
{"d", "day"},
{"w", "wk", "week"},
}
timeMultiplier = map[string]time.Duration{
"ns": time.Nanosecond,
"us": time.Microsecond,
"ms": time.Millisecond,
"s": time.Second,
"m": time.Minute,
"h": time.Hour,
"d": 24 * time.Hour,
"w": 7 * 24 * time.Hour,
}
durationMatcher = regexp.MustCompile(`((\d+)\s*([A-Za-zµ]+))`)
)
// IsDuration returns true if the provided string is a valid duration
func IsDuration(str string) bool {
_, err := ParseDuration(str)
return err == nil
}
// Duration represents a duration
//
// Duration stores a period of time as a nanosecond count, with the largest
// repesentable duration being approximately 290 years.
//
// swagger:strfmt duration
type Duration time.Duration
// MarshalText turns this instance into text
func (d Duration) MarshalText() ([]byte, error) {
return []byte(time.Duration(d).String()), nil
}
// UnmarshalText hydrates this instance from text
func (d *Duration) UnmarshalText(data []byte) error { // validation is performed later on
dd, err := ParseDuration(string(data))
if err != nil {
return err
}
*d = Duration(dd)
return nil
}
// ParseDuration parses a duration from a string, compatible with scala duration syntax
func ParseDuration(cand string) (time.Duration, error) {
if dur, err := time.ParseDuration(cand); err == nil {
return dur, nil
}
var dur time.Duration
ok := false
for _, match := range durationMatcher.FindAllStringSubmatch(cand, -1) {
factor, err := strconv.Atoi(match[2]) // converts string to int
if err != nil {
return 0, err
}
unit := strings.ToLower(strings.TrimSpace(match[3]))
for _, variants := range timeUnits {
last := len(variants) - 1
multiplier := timeMultiplier[variants[0]]
for i, variant := range variants {
if (last == i && strings.HasPrefix(unit, variant)) || strings.EqualFold(variant, unit) {
ok = true
dur += time.Duration(factor) * multiplier
}
}
}
}
if ok {
return dur, nil
}
return 0, fmt.Errorf("unable to parse %s as duration", cand)
}
// Scan reads a Duration value from database driver type.
func (d *Duration) Scan(raw interface{}) error {
switch v := raw.(type) {
// TODO: case []byte: // ?
case int64:
*d = Duration(v)
case float64:
*d = Duration(int64(v))
case nil:
*d = Duration(0)
default:
return fmt.Errorf("cannot sql.Scan() strfmt.Duration from: %#v", v)
}
return nil
}
// String converts this duration to a string
func (d Duration) String() string {
return time.Duration(d).String()
}
// MarshalJSON returns the Duration as JSON
func (d Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Duration(d).String())
}
// UnmarshalJSON sets the Duration from JSON
func (d *Duration) UnmarshalJSON(data []byte) error {
if string(data) == jsonNull {
return nil
}
var dstr string
if err := json.Unmarshal(data, &dstr); err != nil {
return err
}
tt, err := ParseDuration(dstr)
if err != nil {
return err
}
*d = Duration(tt)
return nil
}
// DeepCopyInto copies the receiver and writes its value into out.
func (d *Duration) DeepCopyInto(out *Duration) {
*out = *d
}
// DeepCopy copies the receiver into a new Duration.
func (d *Duration) DeepCopy() *Duration {
if d == nil {
return nil
}
out := new(Duration)
d.DeepCopyInto(out)
return out
}

View File

@ -1,233 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 strfmt
import (
"encoding"
"reflect"
"strings"
"sync"
"k8s.io/kube-openapi/pkg/validation/errors"
)
// Default is the default formats registry
var Default = NewSeededFormats(nil, nil)
// Validator represents a validator for a string format.
type Validator func(string) bool
// Format represents a string format.
//
// All implementations of Format provide a string representation and text
// marshaling/unmarshaling interface to be used by encoders (e.g. encoding/json).
type Format interface {
String() string
encoding.TextMarshaler
encoding.TextUnmarshaler
}
// Registry is a registry of string formats, with a validation method.
type Registry interface {
Add(string, Format, Validator) bool
DelByName(string) bool
GetType(string) (reflect.Type, bool)
ContainsName(string) bool
Validates(string, string) bool
Parse(string, string) (interface{}, error)
}
type knownFormat struct {
Name string
OrigName string
Type reflect.Type
Validator Validator
}
// NameNormalizer is a function that normalizes a format name.
type NameNormalizer func(string) string
// DefaultNameNormalizer removes all dashes
func DefaultNameNormalizer(name string) string {
return strings.Replace(name, "-", "", -1)
}
type defaultFormats struct {
sync.Mutex
data []knownFormat
normalizeName NameNormalizer
}
// NewFormats creates a new formats registry seeded with the values from the default
func NewFormats() Registry {
return NewSeededFormats(Default.(*defaultFormats).data, nil)
}
// NewSeededFormats creates a new formats registry
func NewSeededFormats(seeds []knownFormat, normalizer NameNormalizer) Registry {
if normalizer == nil {
normalizer = DefaultNameNormalizer
}
// copy here, don't modify original
d := append([]knownFormat(nil), seeds...)
return &defaultFormats{
data: d,
normalizeName: normalizer,
}
}
// Add adds a new format, return true if this was a new item instead of a replacement
func (f *defaultFormats) Add(name string, strfmt Format, validator Validator) bool {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
tpe := reflect.TypeOf(strfmt)
if tpe.Kind() == reflect.Ptr {
tpe = tpe.Elem()
}
for i := range f.data {
v := &f.data[i]
if v.Name == nme {
v.Type = tpe
v.Validator = validator
return false
}
}
// turns out it's new after all
f.data = append(f.data, knownFormat{Name: nme, OrigName: name, Type: tpe, Validator: validator})
return true
}
// GetType gets the type for the specified name
func (f *defaultFormats) GetType(name string) (reflect.Type, bool) {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for _, v := range f.data {
if v.Name == nme {
return v.Type, true
}
}
return nil, false
}
// DelByName removes the format by the specified name, returns true when an item was actually removed
func (f *defaultFormats) DelByName(name string) bool {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for i, v := range f.data {
if v.Name == nme {
f.data[i] = knownFormat{} // release
f.data = append(f.data[:i], f.data[i+1:]...)
return true
}
}
return false
}
// DelByFormat removes the specified format, returns true when an item was actually removed
func (f *defaultFormats) DelByFormat(strfmt Format) bool {
f.Lock()
defer f.Unlock()
tpe := reflect.TypeOf(strfmt)
if tpe.Kind() == reflect.Ptr {
tpe = tpe.Elem()
}
for i, v := range f.data {
if v.Type == tpe {
f.data[i] = knownFormat{} // release
f.data = append(f.data[:i], f.data[i+1:]...)
return true
}
}
return false
}
// ContainsName returns true if this registry contains the specified name
func (f *defaultFormats) ContainsName(name string) bool {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for _, v := range f.data {
if v.Name == nme {
return true
}
}
return false
}
// ContainsFormat returns true if this registry contains the specified format
func (f *defaultFormats) ContainsFormat(strfmt Format) bool {
f.Lock()
defer f.Unlock()
tpe := reflect.TypeOf(strfmt)
if tpe.Kind() == reflect.Ptr {
tpe = tpe.Elem()
}
for _, v := range f.data {
if v.Type == tpe {
return true
}
}
return false
}
// Validates passed data against format.
//
// Note that the format name is automatically normalized, e.g. one may
// use "date-time" to use the "datetime" format validator.
func (f *defaultFormats) Validates(name, data string) bool {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for _, v := range f.data {
if v.Name == nme {
return v.Validator(data)
}
}
return false
}
// Parse a string into the appropriate format representation type.
//
// E.g. parsing a string a "date" will return a Date type.
func (f *defaultFormats) Parse(name, data string) (interface{}, error) {
f.Lock()
defer f.Unlock()
nme := f.normalizeName(name)
for _, v := range f.data {
if v.Name == nme {
nw := reflect.New(v.Type).Interface()
if dec, ok := nw.(encoding.TextUnmarshaler); ok {
if err := dec.UnmarshalText([]byte(data)); err != nil {
return nil, err
}
return nw, nil
}
return nil, errors.InvalidTypeName(name)
}
}
return nil, errors.InvalidTypeName(name)
}

View File

@ -1,172 +0,0 @@
// Copyright 2015 go-swagger maintainers
//
// 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 strfmt
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"time"
)
func init() {
dt := DateTime{}
Default.Add("datetime", &dt, IsDateTime)
}
// IsDateTime returns true when the string is a valid date-time
func IsDateTime(str string) bool {
if len(str) < 4 {
return false
}
s := strings.Split(strings.ToLower(str), "t")
if len(s) < 2 || !IsDate(s[0]) {
return false
}
matches := rxDateTime.FindAllStringSubmatch(s[1], -1)
if len(matches) == 0 || len(matches[0]) == 0 {
return false
}
m := matches[0]
res := m[1] <= "23" && m[2] <= "59" && m[3] <= "59"
return res
}
const (
// RFC3339Millis represents a ISO8601 format to millis instead of to nanos
RFC3339Millis = "2006-01-02T15:04:05.000Z07:00"
// RFC3339Micro represents a ISO8601 format to micro instead of to nano
RFC3339Micro = "2006-01-02T15:04:05.000000Z07:00"
// ISO8601LocalTime represents a ISO8601 format to ISO8601 in local time (no timezone)
ISO8601LocalTime = "2006-01-02T15:04:05"
// DateTimePattern pattern to match for the date-time format from http://tools.ietf.org/html/rfc3339#section-5.6
DateTimePattern = `^([0-9]{2}):([0-9]{2}):([0-9]{2})(.[0-9]+)?(z|([+-][0-9]{2}:[0-9]{2}))$`
)
var (
dateTimeFormats = []string{RFC3339Micro, RFC3339Millis, time.RFC3339, time.RFC3339Nano, ISO8601LocalTime}
rxDateTime = regexp.MustCompile(DateTimePattern)
// MarshalFormat sets the time resolution format used for marshaling time (set to milliseconds)
MarshalFormat = RFC3339Millis
)
// ParseDateTime parses a string that represents an ISO8601 time or a unix epoch
func ParseDateTime(data string) (DateTime, error) {
if data == "" {
return NewDateTime(), nil
}
var lastError error
for _, layout := range dateTimeFormats {
dd, err := time.Parse(layout, data)
if err != nil {
lastError = err
continue
}
return DateTime(dd), nil
}
return DateTime{}, lastError
}
// DateTime is a time but it serializes to ISO8601 format with millis
// It knows how to read 3 different variations of a RFC3339 date time.
// Most APIs we encounter want either millisecond or second precision times.
// This just tries to make it worry-free.
//
// swagger:strfmt date-time
type DateTime time.Time
// NewDateTime is a representation of zero value for DateTime type
func NewDateTime() DateTime {
return DateTime(time.Unix(0, 0).UTC())
}
// String converts this time to a string
func (t DateTime) String() string {
return time.Time(t).Format(MarshalFormat)
}
// MarshalText implements the text marshaller interface
func (t DateTime) MarshalText() ([]byte, error) {
return []byte(t.String()), nil
}
// UnmarshalText implements the text unmarshaller interface
func (t *DateTime) UnmarshalText(text []byte) error {
tt, err := ParseDateTime(string(text))
if err != nil {
return err
}
*t = tt
return nil
}
// Scan scans a DateTime value from database driver type.
func (t *DateTime) Scan(raw interface{}) error {
// TODO: case int64: and case float64: ?
switch v := raw.(type) {
case []byte:
return t.UnmarshalText(v)
case string:
return t.UnmarshalText([]byte(v))
case time.Time:
*t = DateTime(v)
case nil:
*t = DateTime{}
default:
return fmt.Errorf("cannot sql.Scan() strfmt.DateTime from: %#v", v)
}
return nil
}
// MarshalJSON returns the DateTime as JSON
func (t DateTime) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(t).Format(MarshalFormat))
}
// UnmarshalJSON sets the DateTime from JSON
func (t *DateTime) UnmarshalJSON(data []byte) error {
if string(data) == jsonNull {
return nil
}
var tstr string
if err := json.Unmarshal(data, &tstr); err != nil {
return err
}
tt, err := ParseDateTime(tstr)
if err != nil {
return err
}
*t = tt
return nil
}
// DeepCopyInto copies the receiver and writes its value into out.
func (t *DateTime) DeepCopyInto(out *DateTime) {
*out = *t
}
// DeepCopy copies the receiver into a new DateTime.
func (t *DateTime) DeepCopy() *DateTime {
if t == nil {
return nil
}
out := new(DateTime)
t.DeepCopyInto(out)
return out
}