diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index dddc97eaf13..dd763fd7f12 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -3299,35 +3299,35 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/aggregator", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/builder", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/common", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/generators", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/handler", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto/validation", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/utils/clock", diff --git a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json index e834f880a3a..098a0454ec4 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json @@ -2008,23 +2008,23 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/builder", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/common", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/handler", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/apimachinery/pkg/api/equality", diff --git a/staging/src/k8s.io/apimachinery/Godeps/Godeps.json b/staging/src/k8s.io/apimachinery/Godeps/Godeps.json index 99a8ffd1eab..404a6cb6e26 100644 --- a/staging/src/k8s.io/apimachinery/Godeps/Godeps.json +++ b/staging/src/k8s.io/apimachinery/Godeps/Godeps.json @@ -180,7 +180,7 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" } ] } diff --git a/staging/src/k8s.io/apiserver/Godeps/Godeps.json b/staging/src/k8s.io/apiserver/Godeps/Godeps.json index 89572f79fac..0b581a1f2b8 100644 --- a/staging/src/k8s.io/apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/apiserver/Godeps/Godeps.json @@ -1736,23 +1736,23 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/builder", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/common", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/handler", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/client-go/discovery", diff --git a/staging/src/k8s.io/client-go/Godeps/Godeps.json b/staging/src/k8s.io/client-go/Godeps/Godeps.json index 84e5eae16df..89f29ca20db 100644 --- a/staging/src/k8s.io/client-go/Godeps/Godeps.json +++ b/staging/src/k8s.io/client-go/Godeps/Godeps.json @@ -580,7 +580,7 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" } ] } diff --git a/staging/src/k8s.io/code-generator/Godeps/Godeps.json b/staging/src/k8s.io/code-generator/Godeps/Godeps.json index d82270eee20..4c96df28aa1 100644 --- a/staging/src/k8s.io/code-generator/Godeps/Godeps.json +++ b/staging/src/k8s.io/code-generator/Godeps/Godeps.json @@ -260,11 +260,11 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/common", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/generators", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" } ] } diff --git a/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json b/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json index b76a40f2f51..ee3fe313481 100644 --- a/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json +++ b/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json @@ -1652,27 +1652,27 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/aggregator", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/builder", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/common", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/handler", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" } ] } diff --git a/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json b/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json index f4b31c71445..51aabfc8622 100644 --- a/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json @@ -1616,23 +1616,23 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/builder", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/common", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/handler", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" } ] } diff --git a/staging/src/k8s.io/sample-controller/Godeps/Godeps.json b/staging/src/k8s.io/sample-controller/Godeps/Godeps.json index 7fcd5bd5e7c..16888629a82 100644 --- a/staging/src/k8s.io/sample-controller/Godeps/Godeps.json +++ b/staging/src/k8s.io/sample-controller/Godeps/Godeps.json @@ -916,7 +916,7 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto", - "Rev": "50ae88d24ede7b8bad68e23c805b5d3da5c8abaf" + "Rev": "f08db293d3ef80052d6513ece19792642a289fea" } ] } diff --git a/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go b/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go index 9fb16e6e9c4..e453a27bdbd 100644 --- a/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go +++ b/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go @@ -159,7 +159,7 @@ func (s *referenceWalker) Start() { } } -// usedDefinitionForSpec returns a map with all used definition in the provided spec as keys and true as values. +// usedDefinitionForSpec returns a map with all used definitions in the provided spec as keys and true as values. func usedDefinitionForSpec(sp *spec.Swagger) map[string]bool { usedDefinitions := map[string]bool{} walkOnAllReferences(func(ref spec.Ref) spec.Ref { @@ -172,7 +172,7 @@ func usedDefinitionForSpec(sp *spec.Swagger) map[string]bool { } // FilterSpecByPaths removes unnecessary paths and definitions used by those paths. -// i.e. if a Path removed by this function, all definition used by it and not used +// i.e. if a Path removed by this function, all definitions used by it and not used // anywhere else will also be removed. func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) { // Walk all references to find all used definitions. This function @@ -335,6 +335,9 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf } for k, v := range source.Definitions { if _, found := dest.Definitions[k]; !found { + if dest.Definitions == nil { + dest.Definitions = spec.Definitions{} + } dest.Definitions[k] = v } } diff --git a/vendor/k8s.io/kube-openapi/pkg/builder/openapi.go b/vendor/k8s.io/kube-openapi/pkg/builder/openapi.go index 3eaed1533e9..78714e8b2ae 100644 --- a/vendor/k8s.io/kube-openapi/pkg/builder/openapi.go +++ b/vendor/k8s.io/kube-openapi/pkg/builder/openapi.go @@ -143,7 +143,7 @@ func (o *openAPI) buildDefinitionRecursively(name string) error { return nil } -// buildDefinitionForType build a definition for a given type and return a referable name to it's definition. +// 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(sample interface{}) (string, error) { diff --git a/vendor/k8s.io/kube-openapi/pkg/common/common.go b/vendor/k8s.io/kube-openapi/pkg/common/common.go index fbe01cabb3b..0d235876deb 100644 --- a/vendor/k8s.io/kube-openapi/pkg/common/common.go +++ b/vendor/k8s.io/kube-openapi/pkg/common/common.go @@ -32,7 +32,7 @@ type OpenAPIDefinition struct { type ReferenceCallback func(path string) spec.Ref -// OpenAPIDefinitions is collection of all definitions. +// GetOpenAPIDefinitions is collection of all definitions. type GetOpenAPIDefinitions func(ReferenceCallback) map[string]OpenAPIDefinition // OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface, diff --git a/vendor/k8s.io/kube-openapi/pkg/generators/BUILD b/vendor/k8s.io/kube-openapi/pkg/generators/BUILD index ed34e8eb93e..cb9e577fe79 100644 --- a/vendor/k8s.io/kube-openapi/pkg/generators/BUILD +++ b/vendor/k8s.io/kube-openapi/pkg/generators/BUILD @@ -2,12 +2,16 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", - srcs = ["openapi.go"], + srcs = [ + "extension.go", + "openapi.go", + ], importpath = "k8s.io/kube-openapi/pkg/generators", visibility = ["//visibility:public"], deps = [ "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/gengo/args:go_default_library", + "//vendor/k8s.io/gengo/examples/set-gen/sets:go_default_library", "//vendor/k8s.io/gengo/generator:go_default_library", "//vendor/k8s.io/gengo/namer:go_default_library", "//vendor/k8s.io/gengo/types:go_default_library", diff --git a/vendor/k8s.io/kube-openapi/pkg/generators/extension.go b/vendor/k8s.io/kube-openapi/pkg/generators/extension.go new file mode 100644 index 00000000000..7f3602408a3 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/generators/extension.go @@ -0,0 +1,128 @@ +/* +Copyright 2018 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 generators + +import ( + "fmt" + "sort" + "strings" + + "k8s.io/gengo/examples/set-gen/sets" + "k8s.io/gengo/types" +) + +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", +} + +// 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 encapsulates information necessary to generate an OpenAPI extension. +type extension struct { + idlTag string // Example: listType + xName string // Example: x-kubernetes-list-type + values []string // Example: [atomic] +} + +func (e extension) validateAllowedValues() error { + // allowedValues not set means no restrictions on values. + allowedValues, exists := allowedExtensionValues[e.xName] + if !exists { + return nil + } + // Check for missing value. + if len(e.values) == 0 { + return fmt.Errorf("%s needs a value, none given.", e.idlTag) + } + // For each extension value, validate that it is allowed. + if !allowedValues.HasAll(e.values...) { + return fmt.Errorf("%v not allowed for %s. Allowed values: %v", + e.values, e.idlTag, allowedValues.List()) + } + return nil +} + +func (e extension) hasMultipleValues() bool { + return len(e.values) > 1 +} + +// Returns sorted list of map keys. Needed for deterministic testing. +func sortedMapKeys(m map[string][]string) []string { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +// Parses comments to return openapi extensions. +// NOTE: Non-empty errors does not mean extensions is empty. +func parseExtensions(comments []string) ([]extension, []error) { + extensions := []extension{} + errors := []error{} + // First, generate extensions from "+k8s:openapi-gen=x-kubernetes-*" annotations. + values := getOpenAPITagValue(comments) + for _, val := range values { + // Example: x-kubernetes-member-tag:member_test + if strings.HasPrefix(val, extensionPrefix) { + parts := strings.SplitN(val, ":", 2) + if len(parts) != 2 { + errors = append(errors, fmt.Errorf("invalid extension value: %v", val)) + continue + } + e := extension{ + idlTag: tagName, // Example: k8s:openapi-gen + xName: parts[0], // Example: x-kubernetes-member-tag + values: []string{parts[1]}, // Example: member_test + } + extensions = append(extensions, e) + } + } + // Next, generate extensions from "idlTags" (e.g. +listType) + tagValues := types.ExtractCommentTags("+", comments) + for _, idlTag := range sortedMapKeys(tagValues) { + xName, 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) + } + extensions = append(extensions, e) + } + return extensions, errors +} diff --git a/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go b/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go index 73e367b3559..deeac757197 100644 --- a/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go +++ b/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go @@ -40,15 +40,17 @@ const tagOptional = "optional" // Known values for the tag. const ( - tagValueTrue = "true" - tagValueFalse = "false" - tagExtensionPrefix = "x-kubernetes-" - tagPatchStrategy = "patchStrategy" - tagPatchMergeKey = "patchMergeKey" - patchStrategyExtensionName = "patch-strategy" - patchMergeKeyExtensionName = "patch-merge-key" + tagValueTrue = "true" + tagValueFalse = "false" ) +// Used for temporary validation of patch struct tags. +// TODO: Remove patch struct tag validation because they we are now consuming OpenAPI on server. +var tempPatchTags = [...]string{ + "patchMergeKey", + "patchStrategy", +} + func getOpenAPITagValue(comments []string) []string { return types.ExtractCommentTags("+", comments)[tagName] } @@ -155,6 +157,7 @@ type openAPIGen struct { // TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions. targetPackage string imports namer.ImportTracker + types []*types.Type context *generator.Context } @@ -169,10 +172,18 @@ func NewOpenAPIGen(sanitizedName string, targetPackage string, context *generato } } +const nameTmpl = "schema_$.type|private$" + func (g *openAPIGen) Namers(c *generator.Context) namer.NameSystems { // Have the raw namer for this file track what it imports. return namer.NameSystems{ "raw": namer.NewRawNamer(g.targetPackage, g.imports), + "private": &namer.NameStrategy{ + Join: func(pre string, in []string, post string) string { + return strings.Join(in, "_") + }, + PrependPackageNames: 4, // enough to fully qualify from k8s.io/api/... + }, } } @@ -181,6 +192,7 @@ func (g *openAPIGen) Filter(c *generator.Context, t *types.Type) bool { if strings.HasPrefix(t.Name.Name, "codecSelfer") { return false } + g.types = append(g.types, t) return true } @@ -215,13 +227,17 @@ func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error { sw := generator.NewSnippetWriter(w, c, "$", "$") sw.Do("func GetOpenAPIDefinitions(ref $.ReferenceCallback|raw$) map[string]$.OpenAPIDefinition|raw$ {\n", argsFromType(nil)) sw.Do("return map[string]$.OpenAPIDefinition|raw${\n", argsFromType(nil)) - return sw.Error() -} -func (g *openAPIGen) Finalize(c *generator.Context, w io.Writer) error { - sw := generator.NewSnippetWriter(w, c, "$", "$") - sw.Do("}\n", nil) + for _, t := range g.types { + err := newOpenAPITypeWriter(sw).generateCall(t) + if err != nil { + return err + } + } + sw.Do("}\n", nil) + sw.Do("}\n\n", nil) + return sw.Error() } @@ -243,10 +259,6 @@ func getJsonTags(m *types.Member) []string { return strings.Split(jsonTag, ",") } -func getPatchTags(m *types.Member) (string, string) { - return reflect.StructTag(m.Tags).Get(tagPatchMergeKey), reflect.StructTag(m.Tags).Get(tagPatchStrategy) -} - func getReferableName(m *types.Member) string { jsonTags := getJsonTags(m) if len(jsonTags) > 0 { @@ -342,7 +354,7 @@ func (g openAPITypeWriter) generateMembers(t *types.Type, required []string) ([] return required, nil } -func (g openAPITypeWriter) generate(t *types.Type) error { +func (g openAPITypeWriter) generateCall(t *types.Type) error { // Only generate for struct type and ignore the rest switch t.Kind { case types.Struct: @@ -350,31 +362,36 @@ func (g openAPITypeWriter) generate(t *types.Type) error { g.Do("\"$.$\": ", t.Name) if hasOpenAPIDefinitionMethod(t) { g.Do("$.type|raw${}.OpenAPIDefinition(),\n", args) + } else { + g.Do(nameTmpl+"(ref),\n", args) + } + } + return g.Error() +} + +func (g openAPITypeWriter) generate(t *types.Type) error { + // Only generate for struct type and ignore the rest + switch t.Kind { + case types.Struct: + if hasOpenAPIDefinitionMethod(t) { + // already invoked directly return nil } + + args := argsFromType(t) + g.Do("func "+nameTmpl+"(ref $.ReferenceCallback|raw$) $.OpenAPIDefinition|raw$ {\n", args) if hasOpenAPIDefinitionMethods(t) { - // Since this generated snippet is part of a map: - // - // map[string]common.OpenAPIDefinition: { - // "TYPE_NAME": { - // Schema: spec.Schema{ ... }, - // }, - // } - // - // For compliance with gofmt -s it's important we elide the - // struct type. The type is implied by the map and will be - // removed otherwise. - g.Do("{\n"+ + g.Do("return $.OpenAPIDefinition|raw${\n"+ "Schema: spec.Schema{\n"+ "SchemaProps: spec.SchemaProps{\n"+ "Type:$.type|raw${}.OpenAPISchemaType(),\n"+ "Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+ "},\n"+ "},\n"+ - "},\n", args) + "}\n}\n\n", args) return nil } - g.Do("{\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil) + g.Do("return $.OpenAPIDefinition|raw${\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", args) g.generateDescription(t.CommentLines) g.Do("Properties: map[string]$.SpecSchemaType|raw${\n", args) required, err := g.generateMembers(t, []string{}) @@ -386,7 +403,7 @@ func (g openAPITypeWriter) generate(t *types.Type) error { g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\"")) } g.Do("},\n", nil) - if err := g.generateExtensions(t.CommentLines); err != nil { + if err := g.generateStructExtensions(t); err != nil { return err } g.Do("},\n", nil) @@ -406,70 +423,74 @@ func (g openAPITypeWriter) generate(t *types.Type) error { } g.Do("\"$.$\",", k) } - g.Do("},\n},\n", nil) + g.Do("},\n}\n}\n\n", nil) } return nil } -func (g openAPITypeWriter) generateExtensions(CommentLines []string) error { - tagValues := getOpenAPITagValue(CommentLines) - type NameValue struct { - Name, Value string - } - extensions := []NameValue{} - for _, val := range tagValues { - if strings.HasPrefix(val, tagExtensionPrefix) { - parts := strings.SplitN(val, ":", 2) - if len(parts) != 2 { - return fmt.Errorf("invalid extension value: %v", val) - } - extensions = append(extensions, NameValue{parts[0], parts[1]}) +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 { + glog.V(2).Infof("[%s]: %s\n", t.String(), e) } } - patchMergeKeyTag, err := getSingleTagsValue(CommentLines, tagPatchMergeKey) - if err != nil { - return err - } - if len(patchMergeKeyTag) > 0 { - extensions = append(extensions, NameValue{tagExtensionPrefix + patchMergeKeyExtensionName, patchMergeKeyTag}) - } - patchStrategyTag, err := getSingleTagsValue(CommentLines, tagPatchStrategy) - if err != nil { - return err - } - if len(patchStrategyTag) > 0 { - extensions = append(extensions, NameValue{tagExtensionPrefix + patchStrategyExtensionName, patchStrategyTag}) + // TODO(seans3): Validate struct extensions here. + g.emitExtensions(extensions) + return nil +} + +func (g openAPITypeWriter) generateMemberExtensions(m *types.Member, parent *types.Type) error { + extensions, errors := parseExtensions(m.CommentLines) + // 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 { + 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 +} + +func (g openAPITypeWriter) emitExtensions(extensions []extension) { + // If any extensions exist, then emit code to create them. if len(extensions) == 0 { - return nil + return } g.Do("VendorExtensible: spec.VendorExtensible{\nExtensions: spec.Extensions{\n", nil) for _, extension := range extensions { - g.Do("\"$.$\": ", extension.Name) - g.Do("\"$.$\",\n", extension.Value) + g.Do("\"$.$\": ", extension.xName) + if extension.hasMultipleValues() { + g.Do("[]string{\n", nil) + } + for _, value := range extension.values { + g.Do("\"$.$\",\n", value) + } + if extension.hasMultipleValues() { + g.Do("},\n", nil) + } } g.Do("},\n},\n", nil) - return nil } // TODO(#44005): Move this validation outside of this generator (probably to policy verifier) func (g openAPITypeWriter) validatePatchTags(m *types.Member, parent *types.Type) error { - patchMergeKeyStructTag, patchStrategyStructTag := getPatchTags(m) - patchMergeKeyCommentTag, err := getSingleTagsValue(m.CommentLines, tagPatchMergeKey) - if err != nil { - return err - } - patchStrategyCommentTag, err := getSingleTagsValue(m.CommentLines, tagPatchStrategy) - if err != nil { - return err - } - if patchMergeKeyStructTag != patchMergeKeyCommentTag { - return fmt.Errorf("patchMergeKey in comment and struct tags should match for member (%s) of (%s)", - m.Name, parent.Name.String()) - } - if patchStrategyStructTag != patchStrategyCommentTag { - return fmt.Errorf("patchStrategy in comment and struct tags should match for member (%s) of (%s)", - m.Name, parent.Name.String()) + // TODO: Remove patch struct tag validation because they we are now consuming OpenAPI on server. + for _, tagKey := range tempPatchTags { + structTagValue := reflect.StructTag(m.Tags).Get(tagKey) + commentTagValue, err := getSingleTagsValue(m.CommentLines, tagKey) + if err != nil { + return err + } + if structTagValue != commentTagValue { + return fmt.Errorf("Tags in comment and struct should match for member (%s) of (%s)", + m.Name, parent.Name.String()) + } } return nil } @@ -526,7 +547,7 @@ func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type) return err } g.Do("\"$.$\": {\n", name) - if err := g.generateExtensions(m.CommentLines); err != nil { + if err := g.generateMemberExtensions(m, parent); err != nil { return err } g.Do("SchemaProps: spec.SchemaProps{\n", nil) diff --git a/vendor/k8s.io/kube-openapi/pkg/handler/handler.go b/vendor/k8s.io/kube-openapi/pkg/handler/handler.go index 5a16cfcab40..63c6f825bdb 100644 --- a/vendor/k8s.io/kube-openapi/pkg/handler/handler.go +++ b/vendor/k8s.io/kube-openapi/pkg/handler/handler.go @@ -84,7 +84,7 @@ func computeETag(data []byte) string { // and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process // are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU. // -// BuildAndRegisterOpenAPIService builds the spec and registers a handler to provides access to it. +// BuildAndRegisterOpenAPIService 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 RegisterOpenAPIService. func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) { spec, err := builder.BuildOpenAPISpec(webServices, config) @@ -98,7 +98,7 @@ func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.Web // and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process // are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU. // -// RegisterOpenAPIService registers a handler to provides access to provided swagger spec. +// RegisterOpenAPIService registers a handler to provide access to provided swagger spec. // Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a // json file and will also serve .pb and .gz files. func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) { @@ -214,7 +214,7 @@ func toGzip(data []byte) []byte { return buf.Bytes() } -// RegisterOpenAPIVersionedService registers a handler to provides access to provided swagger spec. +// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec. func RegisterOpenAPIVersionedService(openapiSpec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) { o := OpenAPIService{} if err := o.UpdateSpec(openapiSpec); err != nil { @@ -264,7 +264,7 @@ func RegisterOpenAPIVersionedService(openapiSpec *spec.Swagger, servePath string return &o, nil } -// BuildAndRegisterOpenAPIVersionedService builds the spec and registers a handler to provides access to it. +// 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. func BuildAndRegisterOpenAPIVersionedService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) { spec, err := builder.BuildOpenAPISpec(webServices, config)