Update version of k8s.io/kube-openapi

This commit is contained in:
jennybuckley 2018-05-24 09:54:03 -07:00
parent 70df783b3c
commit 8ae4b53786
4 changed files with 123 additions and 51 deletions

16
Godeps/Godeps.json generated
View File

@ -3334,35 +3334,35 @@
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/aggregator",
"Rev": "61db125d227fc9d4e373819a059516f32f7f23c7"
"Rev": "86e28c192d2743f0232b9bc5f0a531568ef9f2a5"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/builder",
"Rev": "61db125d227fc9d4e373819a059516f32f7f23c7"
"Rev": "86e28c192d2743f0232b9bc5f0a531568ef9f2a5"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/common",
"Rev": "61db125d227fc9d4e373819a059516f32f7f23c7"
"Rev": "86e28c192d2743f0232b9bc5f0a531568ef9f2a5"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/generators",
"Rev": "61db125d227fc9d4e373819a059516f32f7f23c7"
"Rev": "86e28c192d2743f0232b9bc5f0a531568ef9f2a5"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/handler",
"Rev": "61db125d227fc9d4e373819a059516f32f7f23c7"
"Rev": "86e28c192d2743f0232b9bc5f0a531568ef9f2a5"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/util",
"Rev": "61db125d227fc9d4e373819a059516f32f7f23c7"
"Rev": "86e28c192d2743f0232b9bc5f0a531568ef9f2a5"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "61db125d227fc9d4e373819a059516f32f7f23c7"
"Rev": "86e28c192d2743f0232b9bc5f0a531568ef9f2a5"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto/validation",
"Rev": "61db125d227fc9d4e373819a059516f32f7f23c7"
"Rev": "86e28c192d2743f0232b9bc5f0a531568ef9f2a5"
},
{
"ImportPath": "k8s.io/utils/clock",

View File

@ -45,6 +45,32 @@ type openAPI struct {
// BuildOpenAPISpec builds OpenAPI spec given a list of webservices (containing routes) and common.Config to customize it.
func BuildOpenAPISpec(webServices []*restful.WebService, config *common.Config) (*spec.Swagger, error) {
o := newOpenAPI(config)
err := o.buildPaths(webServices)
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(model)
if err != nil {
return nil, err
}
swagger, err := o.finalizeSwagger()
if err != nil {
return nil, err
}
return &swagger.Definitions, nil
}
// 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{
@ -56,16 +82,6 @@ func BuildOpenAPISpec(webServices []*restful.WebService, config *common.Config)
},
},
}
err := o.init(webServices)
if err != nil {
return nil, err
}
return o.swagger, nil
}
func (o *openAPI) init(webServices []*restful.WebService) error {
if o.config.GetOperationIDAndTags == nil {
o.config.GetOperationIDAndTags = func(r *restful.Route) (string, []string, error) {
return r.Operation, nil, nil
@ -83,22 +99,25 @@ func (o *openAPI) init(webServices []*restful.WebService) error {
if o.config.CommonResponses == nil {
o.config.CommonResponses = map[int]spec.Response{}
}
err := o.buildPaths(webServices)
if err != nil {
return err
}
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 err
return nil, err
}
}
return nil
return o.swagger, nil
}
func getCanonicalizeTypeName(t reflect.Type) string {

View File

@ -27,18 +27,33 @@ import (
const extensionPrefix = "x-kubernetes-"
// Extension tag to openapi extension
var tagToExtension = map[string]string{
"patchMergeKey": "x-kubernetes-patch-merge-key",
"patchStrategy": "x-kubernetes-patch-strategy",
"listType": "x-kubernetes-list-type",
"listMapKey": "x-kubernetes-list-map-keys",
// extensionAttributes encapsulates common traits for particular extensions.
type extensionAttributes struct {
xName string
kind types.Kind
allowedValues sets.String
}
// Enum values per extension
var allowedExtensionValues = map[string]sets.String{
"x-kubernetes-patch-strategy": sets.NewString("merge", "retainKeys"),
"x-kubernetes-list-type": sets.NewString("atomic", "set", "map"),
// Extension tag to openapi extension attributes
var tagToExtension = map[string]extensionAttributes{
"patchMergeKey": extensionAttributes{
xName: "x-kubernetes-patch-merge-key",
kind: types.Slice,
},
"patchStrategy": extensionAttributes{
xName: "x-kubernetes-patch-strategy",
kind: types.Slice,
allowedValues: sets.NewString("merge", "retainKeys"),
},
"listMapKey": extensionAttributes{
xName: "x-kubernetes-list-map-keys",
kind: types.Slice,
},
"listType": extensionAttributes{
xName: "x-kubernetes-list-type",
kind: types.Slice,
allowedValues: sets.NewString("atomic", "set", "map"),
},
}
// Extension encapsulates information necessary to generate an OpenAPI extension.
@ -48,10 +63,25 @@ type extension struct {
values []string // Example: [atomic]
}
func (e extension) hasAllowedValues() bool {
return tagToExtension[e.idlTag].allowedValues.Len() > 0
}
func (e extension) allowedValues() sets.String {
return tagToExtension[e.idlTag].allowedValues
}
func (e extension) hasKind() bool {
return len(tagToExtension[e.idlTag].kind) > 0
}
func (e extension) kind() types.Kind {
return tagToExtension[e.idlTag].kind
}
func (e extension) validateAllowedValues() error {
// allowedValues not set means no restrictions on values.
allowedValues, exists := allowedExtensionValues[e.xName]
if !exists {
if !e.hasAllowedValues() {
return nil
}
// Check for missing value.
@ -59,6 +89,7 @@ func (e extension) validateAllowedValues() error {
return fmt.Errorf("%s needs a value, none given.", e.idlTag)
}
// For each extension value, validate that it is allowed.
allowedValues := e.allowedValues()
if !allowedValues.HasAll(e.values...) {
return fmt.Errorf("%v not allowed for %s. Allowed values: %v",
e.values, e.idlTag, allowedValues.List())
@ -66,6 +97,18 @@ func (e extension) validateAllowedValues() error {
return nil
}
func (e extension) validateType(kind types.Kind) error {
// If this extension class has no kind, then don't validate the type.
if !e.hasKind() {
return nil
}
if kind != e.kind() {
return fmt.Errorf("tag %s on type %v; only allowed on type %v",
e.idlTag, kind, e.kind())
}
return nil
}
func (e extension) hasMultipleValues() bool {
return len(e.values) > 1
}
@ -82,7 +125,9 @@ func sortedMapKeys(m map[string][]string) []string {
return keys
}
// Parses comments to return openapi extensions.
// Parses comments to return openapi extensions. Returns a list of
// extensions which parsed correctly, as well as a list of the
// parse errors. Validating extensions is performed separately.
// NOTE: Non-empty errors does not mean extensions is empty.
func parseExtensions(comments []string) ([]extension, []error) {
extensions := []extension{}
@ -108,21 +153,30 @@ func parseExtensions(comments []string) ([]extension, []error) {
// Next, generate extensions from "idlTags" (e.g. +listType)
tagValues := types.ExtractCommentTags("+", comments)
for _, idlTag := range sortedMapKeys(tagValues) {
xName, exists := tagToExtension[idlTag]
xAttrs, exists := tagToExtension[idlTag]
if !exists {
continue
}
values := tagValues[idlTag]
e := extension{
idlTag: idlTag, // listType
xName: xName, // x-kubernetes-list-type
values: values, // [atomic]
}
if err := e.validateAllowedValues(); err != nil {
// For now, only log the extension validation errors.
errors = append(errors, err)
idlTag: idlTag, // listType
xName: xAttrs.xName, // x-kubernetes-list-type
values: values, // [atomic]
}
extensions = append(extensions, e)
}
return extensions, errors
}
func validateMemberExtensions(extensions []extension, m *types.Member) []error {
errors := []error{}
for _, e := range extensions {
if err := e.validateAllowedValues(); err != nil {
errors = append(errors, err)
}
if err := e.validateType(m.Type.Kind); err != nil {
errors = append(errors, err)
}
}
return errors
}

View File

@ -432,7 +432,7 @@ func (g openAPITypeWriter) generateStructExtensions(t *types.Type) error {
extensions, errors := parseExtensions(t.CommentLines)
// Initially, we will only log struct extension errors.
if len(errors) > 0 {
for e := range errors {
for _, e := range errors {
glog.V(2).Infof("[%s]: %s\n", t.String(), e)
}
}
@ -442,17 +442,16 @@ func (g openAPITypeWriter) generateStructExtensions(t *types.Type) error {
}
func (g openAPITypeWriter) generateMemberExtensions(m *types.Member, parent *types.Type) error {
extensions, errors := parseExtensions(m.CommentLines)
extensions, parseErrors := parseExtensions(m.CommentLines)
validationErrors := validateMemberExtensions(extensions, m)
errors := append(parseErrors, validationErrors...)
// Initially, we will only log member extension errors.
if len(errors) > 0 {
errorPrefix := fmt.Sprintf("[%s] %s:", parent.String(), m.String())
for e := range errors {
for _, e := range errors {
glog.V(2).Infof("%s %s\n", errorPrefix, e)
}
}
// TODO(seans3): Validate member extensions here.
// Example: listType extension is only on a Slice.
// Example: cross-extension validation - listMapKey only makes sense with listType=map
g.emitExtensions(extensions)
return nil
}