update dependencies (#6267)
Signed-off-by: hongming <coder.scala@gmail.com>
This commit is contained in:
64
vendor/k8s.io/kube-openapi/cmd/openapi-gen/args/args.go
generated
vendored
64
vendor/k8s.io/kube-openapi/cmd/openapi-gen/args/args.go
generated
vendored
@@ -18,59 +18,61 @@ package args
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/gengo/args"
|
||||
)
|
||||
|
||||
// CustomArgs is used by the gengo framework to pass args specific to this generator.
|
||||
type CustomArgs struct {
|
||||
// ReportFilename is added to CustomArgs for specifying name of report file used
|
||||
type Args struct {
|
||||
OutputDir string // must be a directory path
|
||||
OutputPkg string // must be a Go import-path
|
||||
OutputFile string
|
||||
|
||||
GoHeaderFile string
|
||||
|
||||
// ReportFilename is added to Args for specifying name of report file used
|
||||
// by API linter. If specified, API rule violations will be printed to report file.
|
||||
// Otherwise default value "-" will be used which indicates stdout.
|
||||
ReportFilename string
|
||||
}
|
||||
|
||||
// NewDefaults returns default arguments for the generator. Returning the arguments instead
|
||||
// New returns default arguments for the generator. Returning the arguments instead
|
||||
// of using default flag parsing allows registering custom arguments afterwards
|
||||
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
|
||||
// Default() sets a couple of flag default values for example the boilerplate.
|
||||
// WithoutDefaultFlagParsing() disables implicit addition of command line flags and parsing,
|
||||
// which allows registering custom arguments afterwards
|
||||
genericArgs := args.Default().WithoutDefaultFlagParsing()
|
||||
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), "k8s.io/kube-openapi/boilerplate/boilerplate.go.txt")
|
||||
|
||||
customArgs := &CustomArgs{}
|
||||
genericArgs.CustomArgs = customArgs
|
||||
func New() *Args {
|
||||
args := &Args{}
|
||||
|
||||
// Default value for report filename is "-", which stands for stdout
|
||||
customArgs.ReportFilename = "-"
|
||||
// Default value for output file base name
|
||||
genericArgs.OutputFileBaseName = "openapi_generated"
|
||||
args.ReportFilename = "-"
|
||||
|
||||
return genericArgs, customArgs
|
||||
return args
|
||||
}
|
||||
|
||||
// AddFlags add the generator flags to the flag set.
|
||||
func (c *CustomArgs) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVarP(&c.ReportFilename, "report-filename", "r", c.ReportFilename, "Name of report file used by API linter to print API violations. Default \"-\" stands for standard output. NOTE that if valid filename other than \"-\" is specified, API linter won't return error on detected API violations. This allows further check of existing API violations without stopping the OpenAPI generation toolchain.")
|
||||
func (args *Args) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&args.OutputDir, "output-dir", "",
|
||||
"the base directory under which to generate results")
|
||||
fs.StringVar(&args.OutputPkg, "output-pkg", "",
|
||||
"the base Go import-path under which to generate results")
|
||||
fs.StringVar(&args.OutputFile, "output-file", "generated.openapi.go",
|
||||
"the name of the file to be generated")
|
||||
fs.StringVar(&args.GoHeaderFile, "go-header-file", "",
|
||||
"the path to a file containing boilerplate header text; the string \"YEAR\" will be replaced with the current 4-digit year")
|
||||
fs.StringVarP(&args.ReportFilename, "report-filename", "r", args.ReportFilename,
|
||||
"Name of report file used by API linter to print API violations. Default \"-\" stands for standard output. NOTE that if valid filename other than \"-\" is specified, API linter won't return error on detected API violations. This allows further check of existing API violations without stopping the OpenAPI generation toolchain.")
|
||||
}
|
||||
|
||||
// Validate checks the given arguments.
|
||||
func Validate(genericArgs *args.GeneratorArgs) error {
|
||||
c, ok := genericArgs.CustomArgs.(*CustomArgs)
|
||||
if !ok {
|
||||
return fmt.Errorf("input arguments don't contain valid custom arguments")
|
||||
func (args *Args) Validate() error {
|
||||
if len(args.OutputDir) == 0 {
|
||||
return fmt.Errorf("--output-dir must be specified")
|
||||
}
|
||||
if len(c.ReportFilename) == 0 {
|
||||
return fmt.Errorf("report filename cannot be empty. specify a valid filename or use \"-\" for stdout")
|
||||
if len(args.OutputPkg) == 0 {
|
||||
return fmt.Errorf("--output-pkg must be specified")
|
||||
}
|
||||
if len(genericArgs.OutputFileBaseName) == 0 {
|
||||
return fmt.Errorf("output file base name cannot be empty")
|
||||
if len(args.OutputFile) == 0 {
|
||||
return fmt.Errorf("--output-file must be specified")
|
||||
}
|
||||
if len(genericArgs.OutputPackagePath) == 0 {
|
||||
return fmt.Errorf("output package cannot be empty")
|
||||
if len(args.ReportFilename) == 0 {
|
||||
return fmt.Errorf("--report-filename must be specified (use \"-\" for stdout)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
25
vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go
generated
vendored
25
vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go
generated
vendored
@@ -24,33 +24,38 @@ import (
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
generatorargs "k8s.io/kube-openapi/cmd/openapi-gen/args"
|
||||
"k8s.io/kube-openapi/pkg/generators"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/gengo/v2"
|
||||
"k8s.io/gengo/v2/generator"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kube-openapi/cmd/openapi-gen/args"
|
||||
"k8s.io/kube-openapi/pkg/generators"
|
||||
)
|
||||
|
||||
func main() {
|
||||
klog.InitFlags(nil)
|
||||
genericArgs, customArgs := generatorargs.NewDefaults()
|
||||
args := args.New()
|
||||
|
||||
genericArgs.AddFlags(pflag.CommandLine)
|
||||
customArgs.AddFlags(pflag.CommandLine)
|
||||
args.AddFlags(pflag.CommandLine)
|
||||
flag.Set("logtostderr", "true")
|
||||
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||
pflag.Parse()
|
||||
|
||||
if err := generatorargs.Validate(genericArgs); err != nil {
|
||||
if err := args.Validate(); err != nil {
|
||||
log.Fatalf("Arguments validation error: %v", err)
|
||||
}
|
||||
|
||||
myTargets := func(context *generator.Context) []generator.Target {
|
||||
return generators.GetTargets(context, args)
|
||||
}
|
||||
|
||||
// Generates the code for the OpenAPIDefinitions.
|
||||
if err := genericArgs.Execute(
|
||||
if err := gengo.Execute(
|
||||
generators.NameSystems(),
|
||||
generators.DefaultNameSystem(),
|
||||
generators.Packages,
|
||||
myTargets,
|
||||
gengo.StdBuildTag,
|
||||
pflag.Args(),
|
||||
); err != nil {
|
||||
log.Fatalf("OpenAPI code generation error: %v", err)
|
||||
}
|
||||
|
||||
3
vendor/k8s.io/kube-openapi/pkg/builder3/openapi.go
generated
vendored
3
vendor/k8s.io/kube-openapi/pkg/builder3/openapi.go
generated
vendored
@@ -326,6 +326,9 @@ func BuildOpenAPISpecFromRoutes(webServices []common.RouteContainer, config *com
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.PostProcessSpec != nil {
|
||||
return config.PostProcessSpec(a.spec)
|
||||
}
|
||||
return a.spec, nil
|
||||
}
|
||||
|
||||
|
||||
3
vendor/k8s.io/kube-openapi/pkg/common/common.go
generated
vendored
3
vendor/k8s.io/kube-openapi/pkg/common/common.go
generated
vendored
@@ -164,6 +164,9 @@ type OpenAPIV3Config struct {
|
||||
// It is an optional function to customize model names.
|
||||
GetDefinitionName func(name string) (string, spec.Extensions)
|
||||
|
||||
// PostProcessSpec runs after the spec is ready to serve. It allows a final modification to the spec before serving.
|
||||
PostProcessSpec func(*spec3.OpenAPI) (*spec3.OpenAPI, error)
|
||||
|
||||
// SecuritySchemes is list of all security schemes for OpenAPI service.
|
||||
SecuritySchemes spec3.SecuritySchemes
|
||||
|
||||
|
||||
6
vendor/k8s.io/kube-openapi/pkg/generators/api_linter.go
generated
vendored
6
vendor/k8s.io/kube-openapi/pkg/generators/api_linter.go
generated
vendored
@@ -25,8 +25,8 @@ import (
|
||||
|
||||
"k8s.io/kube-openapi/pkg/generators/rules"
|
||||
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/gengo/v2/generator"
|
||||
"k8s.io/gengo/v2/types"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
@@ -94,7 +94,7 @@ func newAPIViolationGen() *apiViolationGen {
|
||||
}
|
||||
|
||||
type apiViolationGen struct {
|
||||
generator.DefaultGen
|
||||
generator.GoGenerator
|
||||
|
||||
linter *apiLinter
|
||||
}
|
||||
|
||||
45
vendor/k8s.io/kube-openapi/pkg/generators/config.go
generated
vendored
45
vendor/k8s.io/kube-openapi/pkg/generators/config.go
generated
vendored
@@ -17,16 +17,14 @@ limitations under the License.
|
||||
package generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"path"
|
||||
|
||||
"k8s.io/gengo/args"
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/gengo/v2"
|
||||
"k8s.io/gengo/v2/generator"
|
||||
"k8s.io/gengo/v2/namer"
|
||||
"k8s.io/gengo/v2/types"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
generatorargs "k8s.io/kube-openapi/cmd/openapi-gen/args"
|
||||
"k8s.io/kube-openapi/cmd/openapi-gen/args"
|
||||
)
|
||||
|
||||
type identityNamer struct{}
|
||||
@@ -51,36 +49,31 @@ func DefaultNameSystem() string {
|
||||
return "sorting_namer"
|
||||
}
|
||||
|
||||
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
|
||||
boilerplate, err := arguments.LoadGoBoilerplate()
|
||||
func GetTargets(context *generator.Context, args *args.Args) []generator.Target {
|
||||
boilerplate, err := gengo.GoBoilerplate(args.GoHeaderFile, gengo.StdBuildTag, gengo.StdGeneratedBy)
|
||||
if err != nil {
|
||||
klog.Fatalf("Failed loading boilerplate: %v", err)
|
||||
}
|
||||
header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
|
||||
header = append(header, []byte(
|
||||
`
|
||||
// This file was autogenerated by openapi-gen. Do not edit it manually!
|
||||
|
||||
`)...)
|
||||
|
||||
reportPath := "-"
|
||||
if customArgs, ok := arguments.CustomArgs.(*generatorargs.CustomArgs); ok {
|
||||
reportPath = customArgs.ReportFilename
|
||||
if args.ReportFilename != "" {
|
||||
reportPath = args.ReportFilename
|
||||
}
|
||||
context.FileTypes[apiViolationFileType] = apiViolationFile{
|
||||
unmangledPath: reportPath,
|
||||
}
|
||||
|
||||
return generator.Packages{
|
||||
&generator.DefaultPackage{
|
||||
PackageName: filepath.Base(arguments.OutputPackagePath),
|
||||
PackagePath: arguments.OutputPackagePath,
|
||||
HeaderText: header,
|
||||
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
|
||||
return []generator.Target{
|
||||
&generator.SimpleTarget{
|
||||
PkgName: path.Base(args.OutputPkg), // `path` vs. `filepath` because packages use '/'
|
||||
PkgPath: args.OutputPkg,
|
||||
PkgDir: args.OutputDir,
|
||||
HeaderComment: boilerplate,
|
||||
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
|
||||
return []generator.Generator{
|
||||
newOpenAPIGen(
|
||||
arguments.OutputFileBaseName,
|
||||
arguments.OutputPackagePath,
|
||||
args.OutputFile,
|
||||
args.OutputPkg,
|
||||
),
|
||||
newAPIViolationGen(),
|
||||
}
|
||||
|
||||
25
vendor/k8s.io/kube-openapi/pkg/generators/enum.go
generated
vendored
25
vendor/k8s.io/kube-openapi/pkg/generators/enum.go
generated
vendored
@@ -22,8 +22,9 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/gengo/v2"
|
||||
"k8s.io/gengo/v2/generator"
|
||||
"k8s.io/gengo/v2/types"
|
||||
)
|
||||
|
||||
const tagEnumType = "enum"
|
||||
@@ -121,7 +122,7 @@ func parseEnums(c *generator.Context) enumMap {
|
||||
Value: *c.ConstValue,
|
||||
Comment: strings.Join(c.CommentLines, " "),
|
||||
}
|
||||
enumTypes[enumType.Name].appendValue(value)
|
||||
enumTypes[enumType.Name].addIfNotPresent(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +130,21 @@ func parseEnums(c *generator.Context) enumMap {
|
||||
return enumTypes
|
||||
}
|
||||
|
||||
func (et *enumType) appendValue(value *enumValue) {
|
||||
func (et *enumType) addIfNotPresent(value *enumValue) {
|
||||
// If we already have an enum case with the same value, then ignore this new
|
||||
// one. This can happen if an enum aliases one from another package and
|
||||
// re-exports the cases.
|
||||
for i, existing := range et.Values {
|
||||
if existing.Value == value.Value {
|
||||
|
||||
// Take the value of the longer comment (or some other deterministic tie breaker)
|
||||
if len(existing.Comment) < len(value.Comment) || (len(existing.Comment) == len(value.Comment) && existing.Comment > value.Comment) {
|
||||
et.Values[i] = value
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
et.Values = append(et.Values, value)
|
||||
}
|
||||
|
||||
@@ -155,7 +170,7 @@ func isEnumType(stringType *types.Type, t *types.Type) bool {
|
||||
}
|
||||
|
||||
func hasEnumTag(t *types.Type) bool {
|
||||
return types.ExtractCommentTags("+", t.CommentLines)[tagEnumType] != nil
|
||||
return gengo.ExtractCommentTags("+", t.CommentLines)[tagEnumType] != nil
|
||||
}
|
||||
|
||||
// whitespaceRegex is the regex for consecutive whitespaces.
|
||||
|
||||
7
vendor/k8s.io/kube-openapi/pkg/generators/extension.go
generated
vendored
7
vendor/k8s.io/kube-openapi/pkg/generators/extension.go
generated
vendored
@@ -21,8 +21,9 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/examples/set-gen/sets"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/gengo/v2"
|
||||
"k8s.io/gengo/v2/types"
|
||||
"k8s.io/kube-openapi/pkg/util/sets"
|
||||
)
|
||||
|
||||
const extensionPrefix = "x-kubernetes-"
|
||||
@@ -171,7 +172,7 @@ func parseExtensions(comments []string) ([]extension, []error) {
|
||||
}
|
||||
}
|
||||
// Next, generate extensions from "idlTags" (e.g. +listType)
|
||||
tagValues := types.ExtractCommentTags("+", comments)
|
||||
tagValues := gengo.ExtractCommentTags("+", comments)
|
||||
for _, idlTag := range sortedMapKeys(tagValues) {
|
||||
xAttrs, exists := tagToExtension[idlTag]
|
||||
if !exists {
|
||||
|
||||
613
vendor/k8s.io/kube-openapi/pkg/generators/markers.go
generated
vendored
Normal file
613
vendor/k8s.io/kube-openapi/pkg/generators/markers.go
generated
vendored
Normal file
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
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 generators
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/v2/types"
|
||||
openapi "k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
type CELTag struct {
|
||||
Rule string `json:"rule,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
MessageExpression string `json:"messageExpression,omitempty"`
|
||||
OptionalOldSelf *bool `json:"optionalOldSelf,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
FieldPath string `json:"fieldPath,omitempty"`
|
||||
}
|
||||
|
||||
func (c *CELTag) Validate() error {
|
||||
if c == nil || *c == (CELTag{}) {
|
||||
return fmt.Errorf("empty CEL tag is not allowed")
|
||||
}
|
||||
|
||||
var errs []error
|
||||
if c.Rule == "" {
|
||||
errs = append(errs, fmt.Errorf("rule cannot be empty"))
|
||||
}
|
||||
if c.Message == "" && c.MessageExpression == "" {
|
||||
errs = append(errs, fmt.Errorf("message or messageExpression must be set"))
|
||||
}
|
||||
if c.Message != "" && c.MessageExpression != "" {
|
||||
errs = append(errs, fmt.Errorf("message and messageExpression cannot be set at the same time"))
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// commentTags represents the parsed comment tags for a given type. These types are then used to generate schema validations.
|
||||
// These only include the newer prefixed tags. The older tags are still supported,
|
||||
// but are not included in this struct. Comment Tags are transformed into a
|
||||
// *spec.Schema, which is then combined with the older marker comments to produce
|
||||
// the generated OpenAPI spec.
|
||||
//
|
||||
// List of tags not included in this struct:
|
||||
//
|
||||
// - +optional
|
||||
// - +default
|
||||
// - +listType
|
||||
// - +listMapKeys
|
||||
// - +mapType
|
||||
type commentTags struct {
|
||||
spec.SchemaProps
|
||||
|
||||
CEL []CELTag `json:"cel,omitempty"`
|
||||
|
||||
// Future markers can all be parsed into this centralized struct...
|
||||
// Optional bool `json:"optional,omitempty"`
|
||||
// Default any `json:"default,omitempty"`
|
||||
}
|
||||
|
||||
// Returns the schema for the given CommentTags instance.
|
||||
// This is the final authoritative schema for the comment tags
|
||||
func (c commentTags) ValidationSchema() (*spec.Schema, error) {
|
||||
res := spec.Schema{
|
||||
SchemaProps: c.SchemaProps,
|
||||
}
|
||||
|
||||
if len(c.CEL) > 0 {
|
||||
// Convert the CELTag to a map[string]interface{} via JSON
|
||||
celTagJSON, err := json.Marshal(c.CEL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal CEL tag: %w", err)
|
||||
}
|
||||
var celTagMap []interface{}
|
||||
if err := json.Unmarshal(celTagJSON, &celTagMap); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal CEL tag: %w", err)
|
||||
}
|
||||
|
||||
res.VendorExtensible.AddExtension("x-kubernetes-validations", celTagMap)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// validates the parameters in a CommentTags instance. Returns any errors encountered.
|
||||
func (c commentTags) Validate() error {
|
||||
|
||||
var err error
|
||||
|
||||
if c.MinLength != nil && *c.MinLength < 0 {
|
||||
err = errors.Join(err, fmt.Errorf("minLength cannot be negative"))
|
||||
}
|
||||
if c.MaxLength != nil && *c.MaxLength < 0 {
|
||||
err = errors.Join(err, fmt.Errorf("maxLength cannot be negative"))
|
||||
}
|
||||
if c.MinItems != nil && *c.MinItems < 0 {
|
||||
err = errors.Join(err, fmt.Errorf("minItems cannot be negative"))
|
||||
}
|
||||
if c.MaxItems != nil && *c.MaxItems < 0 {
|
||||
err = errors.Join(err, fmt.Errorf("maxItems cannot be negative"))
|
||||
}
|
||||
if c.MinProperties != nil && *c.MinProperties < 0 {
|
||||
err = errors.Join(err, fmt.Errorf("minProperties cannot be negative"))
|
||||
}
|
||||
if c.MaxProperties != nil && *c.MaxProperties < 0 {
|
||||
err = errors.Join(err, fmt.Errorf("maxProperties cannot be negative"))
|
||||
}
|
||||
if c.Minimum != nil && c.Maximum != nil && *c.Minimum > *c.Maximum {
|
||||
err = errors.Join(err, fmt.Errorf("minimum %f is greater than maximum %f", *c.Minimum, *c.Maximum))
|
||||
}
|
||||
if (c.ExclusiveMinimum || c.ExclusiveMaximum) && c.Minimum != nil && c.Maximum != nil && *c.Minimum == *c.Maximum {
|
||||
err = errors.Join(err, fmt.Errorf("exclusiveMinimum/Maximum cannot be set when minimum == maximum"))
|
||||
}
|
||||
if c.MinLength != nil && c.MaxLength != nil && *c.MinLength > *c.MaxLength {
|
||||
err = errors.Join(err, fmt.Errorf("minLength %d is greater than maxLength %d", *c.MinLength, *c.MaxLength))
|
||||
}
|
||||
if c.MinItems != nil && c.MaxItems != nil && *c.MinItems > *c.MaxItems {
|
||||
err = errors.Join(err, fmt.Errorf("minItems %d is greater than maxItems %d", *c.MinItems, *c.MaxItems))
|
||||
}
|
||||
if c.MinProperties != nil && c.MaxProperties != nil && *c.MinProperties > *c.MaxProperties {
|
||||
err = errors.Join(err, fmt.Errorf("minProperties %d is greater than maxProperties %d", *c.MinProperties, *c.MaxProperties))
|
||||
}
|
||||
if c.Pattern != "" {
|
||||
_, e := regexp.Compile(c.Pattern)
|
||||
if e != nil {
|
||||
err = errors.Join(err, fmt.Errorf("invalid pattern %q: %v", c.Pattern, e))
|
||||
}
|
||||
}
|
||||
if c.MultipleOf != nil && *c.MultipleOf == 0 {
|
||||
err = errors.Join(err, fmt.Errorf("multipleOf cannot be 0"))
|
||||
}
|
||||
|
||||
for i, celTag := range c.CEL {
|
||||
celError := celTag.Validate()
|
||||
if celError == nil {
|
||||
continue
|
||||
}
|
||||
err = errors.Join(err, fmt.Errorf("invalid CEL tag at index %d: %w", i, celError))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Performs type-specific validation for CommentTags porameters. Accepts a Type instance and returns any errors encountered during validation.
|
||||
func (c commentTags) ValidateType(t *types.Type) error {
|
||||
var err error
|
||||
|
||||
resolvedType := resolveAliasAndPtrType(t)
|
||||
typeString, _ := openapi.OpenAPITypeFormat(resolvedType.String()) // will be empty for complicated types
|
||||
|
||||
// Structs and interfaces may dynamically be any type, so we cant validate them
|
||||
// easily. We may be able to if we check that they don't implement all the
|
||||
// override functions, but for now we just skip them.
|
||||
if resolvedType.Kind == types.Interface || resolvedType.Kind == types.Struct {
|
||||
return nil
|
||||
}
|
||||
|
||||
isArray := resolvedType.Kind == types.Slice || resolvedType.Kind == types.Array
|
||||
isMap := resolvedType.Kind == types.Map
|
||||
isString := typeString == "string"
|
||||
isInt := typeString == "integer"
|
||||
isFloat := typeString == "number"
|
||||
|
||||
if c.MaxItems != nil && !isArray {
|
||||
err = errors.Join(err, fmt.Errorf("maxItems can only be used on array types"))
|
||||
}
|
||||
if c.MinItems != nil && !isArray {
|
||||
err = errors.Join(err, fmt.Errorf("minItems can only be used on array types"))
|
||||
}
|
||||
if c.UniqueItems && !isArray {
|
||||
err = errors.Join(err, fmt.Errorf("uniqueItems can only be used on array types"))
|
||||
}
|
||||
if c.MaxProperties != nil && !isMap {
|
||||
err = errors.Join(err, fmt.Errorf("maxProperties can only be used on map types"))
|
||||
}
|
||||
if c.MinProperties != nil && !isMap {
|
||||
err = errors.Join(err, fmt.Errorf("minProperties can only be used on map types"))
|
||||
}
|
||||
if c.MinLength != nil && !isString {
|
||||
err = errors.Join(err, fmt.Errorf("minLength can only be used on string types"))
|
||||
}
|
||||
if c.MaxLength != nil && !isString {
|
||||
err = errors.Join(err, fmt.Errorf("maxLength can only be used on string types"))
|
||||
}
|
||||
if c.Pattern != "" && !isString {
|
||||
err = errors.Join(err, fmt.Errorf("pattern can only be used on string types"))
|
||||
}
|
||||
if c.Minimum != nil && !isInt && !isFloat {
|
||||
err = errors.Join(err, fmt.Errorf("minimum can only be used on numeric types"))
|
||||
}
|
||||
if c.Maximum != nil && !isInt && !isFloat {
|
||||
err = errors.Join(err, fmt.Errorf("maximum can only be used on numeric types"))
|
||||
}
|
||||
if c.MultipleOf != nil && !isInt && !isFloat {
|
||||
err = errors.Join(err, fmt.Errorf("multipleOf can only be used on numeric types"))
|
||||
}
|
||||
if c.ExclusiveMinimum && !isInt && !isFloat {
|
||||
err = errors.Join(err, fmt.Errorf("exclusiveMinimum can only be used on numeric types"))
|
||||
}
|
||||
if c.ExclusiveMaximum && !isInt && !isFloat {
|
||||
err = errors.Join(err, fmt.Errorf("exclusiveMaximum can only be used on numeric types"))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Parses the given comments into a CommentTags type. Validates the parsed comment tags, and returns the result.
|
||||
// Accepts an optional type to validate against, and a prefix to filter out markers not related to validation.
|
||||
// Accepts a prefix to filter out markers not related to validation.
|
||||
// Returns any errors encountered while parsing or validating the comment tags.
|
||||
func ParseCommentTags(t *types.Type, comments []string, prefix string) (*spec.Schema, error) {
|
||||
|
||||
markers, err := parseMarkers(comments, prefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse marker comments: %w", err)
|
||||
}
|
||||
nested, err := nestMarkers(markers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid marker comments: %w", err)
|
||||
}
|
||||
|
||||
// Parse the map into a CommentTags type by marshalling and unmarshalling
|
||||
// as JSON in leiu of an unstructured converter.
|
||||
out, err := json.Marshal(nested)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal marker comments: %w", err)
|
||||
}
|
||||
|
||||
var commentTags commentTags
|
||||
if err = json.Unmarshal(out, &commentTags); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal marker comments: %w", err)
|
||||
}
|
||||
|
||||
// Validate the parsed comment tags
|
||||
validationErrors := commentTags.Validate()
|
||||
|
||||
if t != nil {
|
||||
validationErrors = errors.Join(validationErrors, commentTags.ValidateType(t))
|
||||
}
|
||||
|
||||
if validationErrors != nil {
|
||||
return nil, fmt.Errorf("invalid marker comments: %w", validationErrors)
|
||||
}
|
||||
|
||||
return commentTags.ValidationSchema()
|
||||
}
|
||||
|
||||
var (
|
||||
allowedKeyCharacterSet = `[:_a-zA-Z0-9\[\]\-]`
|
||||
valueEmpty = regexp.MustCompile(fmt.Sprintf(`^(%s*)$`, allowedKeyCharacterSet))
|
||||
valueAssign = regexp.MustCompile(fmt.Sprintf(`^(%s*)=(.*)$`, allowedKeyCharacterSet))
|
||||
valueRawString = regexp.MustCompile(fmt.Sprintf(`^(%s*)>(.*)$`, allowedKeyCharacterSet))
|
||||
)
|
||||
|
||||
// extractCommentTags parses comments for lines of the form:
|
||||
//
|
||||
// 'marker' + "key=value"
|
||||
//
|
||||
// or to specify truthy boolean keys:
|
||||
//
|
||||
// 'marker' + "key"
|
||||
//
|
||||
// Values are optional; "" is the default. A tag can be specified more than
|
||||
// one time and all values are returned. Returns a map with an entry for
|
||||
// for each key and a value.
|
||||
//
|
||||
// Similar to version from gengo, but this version support only allows one
|
||||
// value per key (preferring explicit array indices), supports raw strings
|
||||
// with concatenation, and limits the usable characters allowed in a key
|
||||
// (for simpler parsing).
|
||||
//
|
||||
// Assignments and empty values have the same syntax as from gengo. Raw strings
|
||||
// have the syntax:
|
||||
//
|
||||
// 'marker' + "key>value"
|
||||
// 'marker' + "key>value"
|
||||
//
|
||||
// Successive usages of the same raw string key results in concatenating each
|
||||
// line with `\n` in between. It is an error to use `=` to assing to a previously
|
||||
// assigned key
|
||||
// (in contrast to types.ExtractCommentTags which allows array-typed
|
||||
// values to be specified using `=`).
|
||||
func extractCommentTags(marker string, lines []string) (map[string]string, error) {
|
||||
out := map[string]string{}
|
||||
|
||||
// Used to track the the line immediately prior to the one being iterated.
|
||||
// If there was an invalid or ignored line, these values get reset.
|
||||
lastKey := ""
|
||||
lastIndex := -1
|
||||
lastArrayKey := ""
|
||||
|
||||
var lintErrors []error
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.Trim(line, " ")
|
||||
|
||||
// Track the current value of the last vars to use in this loop iteration
|
||||
// before they are reset for the next iteration.
|
||||
previousKey := lastKey
|
||||
previousArrayKey := lastArrayKey
|
||||
previousIndex := lastIndex
|
||||
|
||||
// Make sure last vars gets reset if we `continue`
|
||||
lastIndex = -1
|
||||
lastArrayKey = ""
|
||||
lastKey = ""
|
||||
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
} else if !strings.HasPrefix(line, marker) {
|
||||
continue
|
||||
}
|
||||
|
||||
line = strings.TrimPrefix(line, marker)
|
||||
|
||||
key := ""
|
||||
value := ""
|
||||
|
||||
if matches := valueAssign.FindStringSubmatch(line); matches != nil {
|
||||
key = matches[1]
|
||||
value = matches[2]
|
||||
|
||||
// If key exists, throw error.
|
||||
// Some of the old kube open-api gen marker comments like
|
||||
// `+listMapKeys` allowed a list to be specified by writing key=value
|
||||
// multiple times.
|
||||
//
|
||||
// This is not longer supported for the prefixed marker comments.
|
||||
// This is to prevent confusion with the new array syntax which
|
||||
// supports lists of objects.
|
||||
//
|
||||
// The old marker comments like +listMapKeys will remain functional,
|
||||
// but new markers will not support it.
|
||||
if _, ok := out[key]; ok {
|
||||
return nil, fmt.Errorf("cannot have multiple values for key '%v'", key)
|
||||
}
|
||||
|
||||
} else if matches := valueEmpty.FindStringSubmatch(line); matches != nil {
|
||||
key = matches[1]
|
||||
value = ""
|
||||
|
||||
} else if matches := valueRawString.FindStringSubmatch(line); matches != nil {
|
||||
toAdd := strings.Trim(string(matches[2]), " ")
|
||||
|
||||
key = matches[1]
|
||||
|
||||
// First usage as a raw string.
|
||||
if existing, exists := out[key]; !exists {
|
||||
|
||||
// Encode the raw string as JSON to ensure that it is properly escaped.
|
||||
valueBytes, err := json.Marshal(toAdd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid value for key %v: %w", key, err)
|
||||
}
|
||||
|
||||
value = string(valueBytes)
|
||||
} else if key != previousKey {
|
||||
// Successive usages of the same key of a raw string must be
|
||||
// consecutive
|
||||
return nil, fmt.Errorf("concatenations to key '%s' must be consecutive with its assignment", key)
|
||||
} else {
|
||||
// If it is a consecutive repeat usage, concatenate to the
|
||||
// existing value.
|
||||
//
|
||||
// Decode JSON string, append to it, re-encode JSON string.
|
||||
// Kinda janky but this is a code-generator...
|
||||
var unmarshalled string
|
||||
if err := json.Unmarshal([]byte(existing), &unmarshalled); err != nil {
|
||||
return nil, fmt.Errorf("invalid value for key %v: %w", key, err)
|
||||
} else {
|
||||
unmarshalled += "\n" + toAdd
|
||||
valueBytes, err := json.Marshal(unmarshalled)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid value for key %v: %w", key, err)
|
||||
}
|
||||
|
||||
value = string(valueBytes)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Comment has the correct prefix, but incorrect syntax, so it is an
|
||||
// error
|
||||
return nil, fmt.Errorf("invalid marker comment does not match expected `+key=<json formatted value>` pattern: %v", line)
|
||||
}
|
||||
|
||||
out[key] = value
|
||||
lastKey = key
|
||||
|
||||
// Lint the array subscript for common mistakes. This only lints the last
|
||||
// array index used, (since we do not have a need for nested arrays yet
|
||||
// in markers)
|
||||
if arrayPath, index, hasSubscript, err := extractArraySubscript(key); hasSubscript {
|
||||
// If index is non-zero, check that that previous line was for the same
|
||||
// key and either the same or previous index
|
||||
if err != nil {
|
||||
lintErrors = append(lintErrors, fmt.Errorf("error parsing %v: expected integer index in key '%v'", line, key))
|
||||
} else if previousArrayKey != arrayPath && index != 0 {
|
||||
lintErrors = append(lintErrors, fmt.Errorf("error parsing %v: non-consecutive index %v for key '%v'", line, index, arrayPath))
|
||||
} else if index != previousIndex+1 && index != previousIndex {
|
||||
lintErrors = append(lintErrors, fmt.Errorf("error parsing %v: non-consecutive index %v for key '%v'", line, index, arrayPath))
|
||||
}
|
||||
|
||||
lastIndex = index
|
||||
lastArrayKey = arrayPath
|
||||
}
|
||||
}
|
||||
|
||||
if len(lintErrors) > 0 {
|
||||
return nil, errors.Join(lintErrors...)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Extracts and parses the given marker comments into a map of key -> value.
|
||||
// Accepts a prefix to filter out markers not related to validation.
|
||||
// The prefix is removed from the key in the returned map.
|
||||
// Empty keys and invalid values will return errors, refs are currently unsupported and will be skipped.
|
||||
func parseMarkers(markerComments []string, prefix string) (map[string]any, error) {
|
||||
markers, err := extractCommentTags(prefix, markerComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the values as JSON
|
||||
result := map[string]any{}
|
||||
for key, value := range markers {
|
||||
var unmarshalled interface{}
|
||||
|
||||
if len(key) == 0 {
|
||||
return nil, fmt.Errorf("cannot have empty key for marker comment")
|
||||
} else if _, ok := parseSymbolReference(value, ""); ok {
|
||||
// Skip ref markers
|
||||
continue
|
||||
} else if len(value) == 0 {
|
||||
// Empty value means key is implicitly a bool
|
||||
result[key] = true
|
||||
} else if err := json.Unmarshal([]byte(value), &unmarshalled); err != nil {
|
||||
// Not valid JSON, throw error
|
||||
return nil, fmt.Errorf("failed to parse value for key %v as JSON: %w", key, err)
|
||||
} else {
|
||||
// Is is valid JSON, use as a JSON value
|
||||
result[key] = unmarshalled
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Converts a map of:
|
||||
//
|
||||
// "a:b:c": 1
|
||||
// "a:b:d": 2
|
||||
// "a:e": 3
|
||||
// "f": 4
|
||||
//
|
||||
// Into:
|
||||
//
|
||||
// map[string]any{
|
||||
// "a": map[string]any{
|
||||
// "b": map[string]any{
|
||||
// "c": 1,
|
||||
// "d": 2,
|
||||
// },
|
||||
// "e": 3,
|
||||
// },
|
||||
// "f": 4,
|
||||
// }
|
||||
//
|
||||
// Returns a list of joined errors for any invalid keys. See putNestedValue for more details.
|
||||
func nestMarkers(markers map[string]any) (map[string]any, error) {
|
||||
nested := make(map[string]any)
|
||||
var errs []error
|
||||
for key, value := range markers {
|
||||
var err error
|
||||
keys := strings.Split(key, ":")
|
||||
|
||||
if err = putNestedValue(nested, keys, value); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
return nested, nil
|
||||
}
|
||||
|
||||
// Recursively puts a value into the given keypath, creating intermediate maps
|
||||
// and slices as needed. If a key is of the form `foo[bar]`, then bar will be
|
||||
// treated as an index into the array foo. If bar is not a valid integer, putNestedValue returns an error.
|
||||
func putNestedValue(m map[string]any, k []string, v any) error {
|
||||
if len(k) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
key := k[0]
|
||||
rest := k[1:]
|
||||
|
||||
// Array case
|
||||
if arrayKeyWithoutSubscript, index, hasSubscript, err := extractArraySubscript(key); err != nil {
|
||||
return fmt.Errorf("error parsing subscript for key %v: %w", key, err)
|
||||
} else if hasSubscript {
|
||||
key = arrayKeyWithoutSubscript
|
||||
var arrayDestination []any
|
||||
if existing, ok := m[key]; !ok {
|
||||
arrayDestination = make([]any, index+1)
|
||||
} else if existing, ok := existing.([]any); !ok {
|
||||
// Error case. Existing isn't of correct type. Can happen if
|
||||
// someone is subscripting a field that was previously not an array
|
||||
return fmt.Errorf("expected []any at key %v, got %T", key, existing)
|
||||
} else if index >= len(existing) {
|
||||
// Ensure array is big enough
|
||||
arrayDestination = append(existing, make([]any, index-len(existing)+1)...)
|
||||
} else {
|
||||
arrayDestination = existing
|
||||
}
|
||||
|
||||
m[key] = arrayDestination
|
||||
if arrayDestination[index] == nil {
|
||||
// Doesn't exist case, create the destination.
|
||||
// Assumes the destination is a map for now. Theoretically could be
|
||||
// extended to support arrays of arrays, but that's not needed yet.
|
||||
destination := make(map[string]any)
|
||||
arrayDestination[index] = destination
|
||||
if err = putNestedValue(destination, rest, v); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if dst, ok := arrayDestination[index].(map[string]any); ok {
|
||||
// Already exists case, correct type
|
||||
if putNestedValue(dst, rest, v); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Already exists, incorrect type. Error
|
||||
// This shouldn't be possible.
|
||||
return fmt.Errorf("expected map at %v[%v], got %T", key, index, arrayDestination[index])
|
||||
}
|
||||
|
||||
return nil
|
||||
} else if len(rest) == 0 {
|
||||
// Base case. Single key. Just set into destination
|
||||
m[key] = v
|
||||
return nil
|
||||
}
|
||||
|
||||
if existing, ok := m[key]; !ok {
|
||||
destination := make(map[string]any)
|
||||
m[key] = destination
|
||||
return putNestedValue(destination, rest, v)
|
||||
} else if destination, ok := existing.(map[string]any); ok {
|
||||
return putNestedValue(destination, rest, v)
|
||||
} else {
|
||||
// Error case. Existing isn't of correct type. Can happen if prior comment
|
||||
// referred to value as an error
|
||||
return fmt.Errorf("expected map[string]any at key %v, got %T", key, existing)
|
||||
}
|
||||
}
|
||||
|
||||
// extractArraySubscript extracts the left array subscript from a key of
|
||||
// the form `foo[bar][baz]` -> "bar".
|
||||
// Returns the key without the subscript, the index, and a bool indicating if
|
||||
// the key had a subscript.
|
||||
// If the key has a subscript, but the subscript is not a valid integer, returns an error.
|
||||
//
|
||||
// This can be adapted to support multidimensional subscripts probably fairly
|
||||
// easily by retuning a list of ints
|
||||
func extractArraySubscript(str string) (string, int, bool, error) {
|
||||
subscriptIdx := strings.Index(str, "[")
|
||||
if subscriptIdx == -1 {
|
||||
return "", -1, false, nil
|
||||
}
|
||||
|
||||
subscript := strings.Split(str[subscriptIdx+1:], "]")[0]
|
||||
if len(subscript) == 0 {
|
||||
return "", -1, false, fmt.Errorf("empty subscript not allowed")
|
||||
}
|
||||
|
||||
index, err := strconv.Atoi(subscript)
|
||||
if err != nil {
|
||||
return "", -1, false, fmt.Errorf("expected integer index in key %v", str)
|
||||
} else if index < 0 {
|
||||
return "", -1, false, fmt.Errorf("subscript '%v' is invalid. index must be positive", subscript)
|
||||
}
|
||||
|
||||
return str[:subscriptIdx], index, true, nil
|
||||
}
|
||||
269
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
269
vendor/k8s.io/kube-openapi/pkg/generators/openapi.go
generated
vendored
@@ -21,23 +21,27 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
defaultergen "k8s.io/gengo/examples/defaulter-gen/generators"
|
||||
"k8s.io/gengo/generator"
|
||||
"k8s.io/gengo/namer"
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/gengo/v2"
|
||||
"k8s.io/gengo/v2/generator"
|
||||
"k8s.io/gengo/v2/namer"
|
||||
"k8s.io/gengo/v2/types"
|
||||
openapi "k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// This is the comment tag that carries parameters for open API generation.
|
||||
const tagName = "k8s:openapi-gen"
|
||||
const markerPrefix = "+k8s:validation:"
|
||||
const tagOptional = "optional"
|
||||
const tagRequired = "required"
|
||||
const tagDefault = "default"
|
||||
|
||||
// Known values for the tag.
|
||||
@@ -54,11 +58,11 @@ var tempPatchTags = [...]string{
|
||||
}
|
||||
|
||||
func getOpenAPITagValue(comments []string) []string {
|
||||
return types.ExtractCommentTags("+", comments)[tagName]
|
||||
return gengo.ExtractCommentTags("+", comments)[tagName]
|
||||
}
|
||||
|
||||
func getSingleTagsValue(comments []string, tag string) (string, error) {
|
||||
tags, ok := types.ExtractCommentTags("+", comments)[tag]
|
||||
tags, ok := gengo.ExtractCommentTags("+", comments)[tag]
|
||||
if !ok || len(tags) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
@@ -78,14 +82,25 @@ func hasOpenAPITagValue(comments []string, value string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// hasOptionalTag returns true if the member has +optional in its comments or
|
||||
// omitempty in its json tags.
|
||||
func hasOptionalTag(m *types.Member) bool {
|
||||
hasOptionalCommentTag := types.ExtractCommentTags(
|
||||
// isOptional returns error if the member has +optional and +required in
|
||||
// its comments. If +optional is present it returns true. If +required is present
|
||||
// it returns false. Otherwise, it returns true if `omitempty` JSON tag is present
|
||||
func isOptional(m *types.Member) (bool, error) {
|
||||
hasOptionalCommentTag := gengo.ExtractCommentTags(
|
||||
"+", m.CommentLines)[tagOptional] != nil
|
||||
hasOptionalJsonTag := strings.Contains(
|
||||
reflect.StructTag(m.Tags).Get("json"), "omitempty")
|
||||
return hasOptionalCommentTag || hasOptionalJsonTag
|
||||
hasRequiredCommentTag := gengo.ExtractCommentTags(
|
||||
"+", m.CommentLines)[tagRequired] != nil
|
||||
if hasOptionalCommentTag && hasRequiredCommentTag {
|
||||
return false, fmt.Errorf("member %s cannot be both optional and required", m.Name)
|
||||
} else if hasRequiredCommentTag {
|
||||
return false, nil
|
||||
} else if hasOptionalCommentTag {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// If neither +optional nor +required is present in the comments,
|
||||
// infer optional from the json tags.
|
||||
return strings.Contains(reflect.StructTag(m.Tags).Get("json"), "omitempty"), nil
|
||||
}
|
||||
|
||||
func apiTypeFilterFunc(c *generator.Context, t *types.Type) bool {
|
||||
@@ -110,16 +125,16 @@ const (
|
||||
|
||||
// openApiGen produces a file with auto-generated OpenAPI functions.
|
||||
type openAPIGen struct {
|
||||
generator.DefaultGen
|
||||
generator.GoGenerator
|
||||
// TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions.
|
||||
targetPackage string
|
||||
imports namer.ImportTracker
|
||||
}
|
||||
|
||||
func newOpenAPIGen(sanitizedName string, targetPackage string) generator.Generator {
|
||||
func newOpenAPIGen(outputFilename string, targetPackage string) generator.Generator {
|
||||
return &openAPIGen{
|
||||
DefaultGen: generator.DefaultGen{
|
||||
OptionalName: sanitizedName,
|
||||
GoGenerator: generator.GoGenerator{
|
||||
OutputFilename: outputFilename,
|
||||
},
|
||||
imports: generator.NewImportTrackerForPackage(targetPackage),
|
||||
targetPackage: targetPackage,
|
||||
@@ -141,16 +156,6 @@ func (g *openAPIGen) Namers(c *generator.Context) namer.NameSystems {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *openAPIGen) isOtherPackage(pkg string) bool {
|
||||
if pkg == g.targetPackage {
|
||||
return false
|
||||
}
|
||||
if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *openAPIGen) Imports(c *generator.Context) []string {
|
||||
importLines := []string{}
|
||||
for _, singleImport := range g.imports.ImportLines() {
|
||||
@@ -292,7 +297,8 @@ func hasOpenAPIV3OneOfMethod(t *types.Type) bool {
|
||||
|
||||
// typeShortName returns short package name (e.g. the name x appears in package x definition) dot type name.
|
||||
func typeShortName(t *types.Type) string {
|
||||
return filepath.Base(t.Name.Package) + "." + t.Name.Name
|
||||
// `path` vs. `filepath` because packages use '/'
|
||||
return path.Base(t.Name.Package) + "." + t.Name.Name
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateMembers(t *types.Type, required []string) ([]string, error) {
|
||||
@@ -315,7 +321,10 @@ func (g openAPITypeWriter) generateMembers(t *types.Type, required []string) ([]
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if !hasOptionalTag(&m) {
|
||||
if isOptional, err := isOptional(&m); err != nil {
|
||||
klog.Errorf("Error when generating: %v, %v\n", name, m)
|
||||
return required, err
|
||||
} else if !isOptional {
|
||||
required = append(required, name)
|
||||
}
|
||||
if err = g.generateProperty(&m, t); err != nil {
|
||||
@@ -353,10 +362,76 @@ func (g openAPITypeWriter) generateCall(t *types.Type) error {
|
||||
return g.Error()
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateValueValidations(vs *spec.SchemaProps) error {
|
||||
|
||||
if vs == nil {
|
||||
return nil
|
||||
}
|
||||
args := generator.Args{
|
||||
"ptrTo": &types.Type{
|
||||
Name: types.Name{
|
||||
Package: "k8s.io/utils/ptr",
|
||||
Name: "To",
|
||||
}},
|
||||
"spec": vs,
|
||||
}
|
||||
if vs.Minimum != nil {
|
||||
g.Do("Minimum: $.ptrTo|raw$[float64]($.spec.Minimum$),\n", args)
|
||||
}
|
||||
if vs.Maximum != nil {
|
||||
g.Do("Maximum: $.ptrTo|raw$[float64]($.spec.Maximum$),\n", args)
|
||||
}
|
||||
if vs.ExclusiveMinimum {
|
||||
g.Do("ExclusiveMinimum: true,\n", args)
|
||||
}
|
||||
if vs.ExclusiveMaximum {
|
||||
g.Do("ExclusiveMaximum: true,\n", args)
|
||||
}
|
||||
if vs.MinLength != nil {
|
||||
g.Do("MinLength: $.ptrTo|raw$[int64]($.spec.MinLength$),\n", args)
|
||||
}
|
||||
if vs.MaxLength != nil {
|
||||
g.Do("MaxLength: $.ptrTo|raw$[int64]($.spec.MaxLength$),\n", args)
|
||||
}
|
||||
|
||||
if vs.MinProperties != nil {
|
||||
g.Do("MinProperties: $.ptrTo|raw$[int64]($.spec.MinProperties$),\n", args)
|
||||
}
|
||||
if vs.MaxProperties != nil {
|
||||
g.Do("MaxProperties: $.ptrTo|raw$[int64]($.spec.MaxProperties$),\n", args)
|
||||
}
|
||||
if len(vs.Pattern) > 0 {
|
||||
p, err := json.Marshal(vs.Pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("Pattern: $.$,\n", string(p))
|
||||
}
|
||||
if vs.MultipleOf != nil {
|
||||
g.Do("MultipleOf: $.ptrTo|raw$[float64]($.spec.MultipleOf$),\n", args)
|
||||
}
|
||||
if vs.MinItems != nil {
|
||||
g.Do("MinItems: $.ptrTo|raw$[int64]($.spec.MinItems$),\n", args)
|
||||
}
|
||||
if vs.MaxItems != nil {
|
||||
g.Do("MaxItems: $.ptrTo|raw$[int64]($.spec.MaxItems$),\n", args)
|
||||
}
|
||||
if vs.UniqueItems {
|
||||
g.Do("UniqueItems: true,\n", nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generate(t *types.Type) error {
|
||||
// Only generate for struct type and ignore the rest
|
||||
switch t.Kind {
|
||||
case types.Struct:
|
||||
validationSchema, err := ParseCommentTags(t, t.CommentLines, markerPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hasV2Definition := hasOpenAPIDefinitionMethod(t)
|
||||
hasV2DefinitionTypeAndFormat := hasOpenAPIDefinitionMethods(t)
|
||||
hasV3OneOfTypes := hasOpenAPIV3OneOfMethod(t)
|
||||
@@ -376,10 +451,17 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
|
||||
"SchemaProps: spec.SchemaProps{\n", args)
|
||||
g.generateDescription(t.CommentLines)
|
||||
g.Do("Type:$.type|raw${}.OpenAPISchemaType(),\n"+
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+
|
||||
"},\n"+
|
||||
"},\n"+
|
||||
"})\n}\n\n", args)
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n", args)
|
||||
err = g.generateValueValidations(&validationSchema.SchemaProps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
if err := g.generateStructExtensions(t, validationSchema.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
g.Do("})\n}\n\n", args)
|
||||
return nil
|
||||
case hasV2DefinitionTypeAndFormat && hasV3OneOfTypes:
|
||||
// generate v3 def.
|
||||
@@ -388,20 +470,34 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
|
||||
"SchemaProps: spec.SchemaProps{\n", args)
|
||||
g.generateDescription(t.CommentLines)
|
||||
g.Do("OneOf:common.GenerateOpenAPIV3OneOfSchema($.type|raw${}.OpenAPIV3OneOfTypes()),\n"+
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+
|
||||
"},\n"+
|
||||
"},\n"+
|
||||
"},", args)
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n", args)
|
||||
err = g.generateValueValidations(&validationSchema.SchemaProps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
if err := g.generateStructExtensions(t, validationSchema.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
g.Do("},", args)
|
||||
// generate v2 def.
|
||||
g.Do("$.OpenAPIDefinition|raw${\n"+
|
||||
"Schema: spec.Schema{\n"+
|
||||
"SchemaProps: spec.SchemaProps{\n", args)
|
||||
g.generateDescription(t.CommentLines)
|
||||
g.Do("Type:$.type|raw${}.OpenAPISchemaType(),\n"+
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+
|
||||
"},\n"+
|
||||
"},\n"+
|
||||
"})\n}\n\n", args)
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n", args)
|
||||
err = g.generateValueValidations(&validationSchema.SchemaProps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
if err := g.generateStructExtensions(t, validationSchema.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
g.Do("})\n}\n\n", args)
|
||||
return nil
|
||||
case hasV2DefinitionTypeAndFormat:
|
||||
g.Do("return $.OpenAPIDefinition|raw${\n"+
|
||||
@@ -409,18 +505,30 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
|
||||
"SchemaProps: spec.SchemaProps{\n", args)
|
||||
g.generateDescription(t.CommentLines)
|
||||
g.Do("Type:$.type|raw${}.OpenAPISchemaType(),\n"+
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+
|
||||
"},\n"+
|
||||
"},\n"+
|
||||
"}\n}\n\n", args)
|
||||
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n", args)
|
||||
err = g.generateValueValidations(&validationSchema.SchemaProps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
if err := g.generateStructExtensions(t, validationSchema.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
g.Do("}\n}\n\n", args)
|
||||
return nil
|
||||
case hasV3OneOfTypes:
|
||||
// having v3 oneOf types without custom v2 type or format does not make sense.
|
||||
return fmt.Errorf("type %q has v3 one of types but not v2 type or format", t.Name)
|
||||
}
|
||||
|
||||
g.Do("return $.OpenAPIDefinition|raw${\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", args)
|
||||
g.generateDescription(t.CommentLines)
|
||||
g.Do("Type: []string{\"object\"},\n", nil)
|
||||
err = g.generateValueValidations(&validationSchema.SchemaProps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write members into a temporary buffer, in order to postpone writing out the Properties field. We only do
|
||||
// that if it is not empty.
|
||||
@@ -441,7 +549,7 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
|
||||
g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\""))
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
if err := g.generateStructExtensions(t); err != nil {
|
||||
if err := g.generateStructExtensions(t, validationSchema.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
@@ -474,7 +582,7 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateStructExtensions(t *types.Type) error {
|
||||
func (g openAPITypeWriter) generateStructExtensions(t *types.Type, otherExtensions map[string]interface{}) error {
|
||||
extensions, errors := parseExtensions(t.CommentLines)
|
||||
// Initially, we will only log struct extension errors.
|
||||
if len(errors) > 0 {
|
||||
@@ -490,11 +598,11 @@ func (g openAPITypeWriter) generateStructExtensions(t *types.Type) error {
|
||||
}
|
||||
|
||||
// TODO(seans3): Validate struct extensions here.
|
||||
g.emitExtensions(extensions, unions)
|
||||
g.emitExtensions(extensions, unions, otherExtensions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) generateMemberExtensions(m *types.Member, parent *types.Type) error {
|
||||
func (g openAPITypeWriter) generateMemberExtensions(m *types.Member, parent *types.Type, otherExtensions map[string]interface{}) error {
|
||||
extensions, parseErrors := parseExtensions(m.CommentLines)
|
||||
validationErrors := validateMemberExtensions(extensions, m)
|
||||
errors := append(parseErrors, validationErrors...)
|
||||
@@ -505,13 +613,13 @@ func (g openAPITypeWriter) generateMemberExtensions(m *types.Member, parent *typ
|
||||
klog.V(2).Infof("%s %s\n", errorPrefix, e)
|
||||
}
|
||||
}
|
||||
g.emitExtensions(extensions, nil)
|
||||
g.emitExtensions(extensions, nil, otherExtensions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g openAPITypeWriter) emitExtensions(extensions []extension, unions []union) {
|
||||
func (g openAPITypeWriter) emitExtensions(extensions []extension, unions []union, otherExtensions map[string]interface{}) {
|
||||
// If any extensions exist, then emit code to create them.
|
||||
if len(extensions) == 0 && len(unions) == 0 {
|
||||
if len(extensions) == 0 && len(unions) == 0 && len(otherExtensions) == 0 {
|
||||
return
|
||||
}
|
||||
g.Do("VendorExtensible: spec.VendorExtensible{\nExtensions: spec.Extensions{\n", nil)
|
||||
@@ -534,6 +642,16 @@ func (g openAPITypeWriter) emitExtensions(extensions []extension, unions []union
|
||||
}
|
||||
g.Do("},\n", nil)
|
||||
}
|
||||
|
||||
if len(otherExtensions) > 0 {
|
||||
for k, v := range otherExtensions {
|
||||
g.Do("$.key$: $.value$,\n", map[string]interface{}{
|
||||
"key": fmt.Sprintf("%#v", k),
|
||||
"value": fmt.Sprintf("%#v", v),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
g.Do("},\n},\n", nil)
|
||||
}
|
||||
|
||||
@@ -585,7 +703,7 @@ func defaultFromComments(comments []string, commentPath string, t *types.Type) (
|
||||
}
|
||||
|
||||
var i interface{}
|
||||
if id, ok := defaultergen.ParseSymbolReference(tag, commentPath); ok {
|
||||
if id, ok := parseSymbolReference(tag, commentPath); ok {
|
||||
klog.Errorf("%v, %v", id, commentPath)
|
||||
return nil, &id, nil
|
||||
} else if err := json.Unmarshal([]byte(tag), &i); err != nil {
|
||||
@@ -594,6 +712,31 @@ func defaultFromComments(comments []string, commentPath string, t *types.Type) (
|
||||
return i, nil, nil
|
||||
}
|
||||
|
||||
var refRE = regexp.MustCompile(`^ref\((?P<reference>[^"]+)\)$`)
|
||||
var refREIdentIndex = refRE.SubexpIndex("reference")
|
||||
|
||||
// parseSymbolReference looks for strings that match one of the following:
|
||||
// - ref(Ident)
|
||||
// - ref(pkgpath.Ident)
|
||||
// If the input string matches either of these, it will return the (optional)
|
||||
// pkgpath, the Ident, and true. Otherwise it will return empty strings and
|
||||
// false.
|
||||
//
|
||||
// This is borrowed from k8s.io/code-generator.
|
||||
func parseSymbolReference(s, sourcePackage string) (types.Name, bool) {
|
||||
matches := refRE.FindStringSubmatch(s)
|
||||
if len(matches) < refREIdentIndex || matches[refREIdentIndex] == "" {
|
||||
return types.Name{}, false
|
||||
}
|
||||
|
||||
contents := matches[refREIdentIndex]
|
||||
name := types.ParseFullyQualifiedName(contents)
|
||||
if len(name.Package) == 0 {
|
||||
name.Package = sourcePackage
|
||||
}
|
||||
return name, true
|
||||
}
|
||||
|
||||
func implementsCustomUnmarshalling(t *types.Type) bool {
|
||||
switch t.Kind {
|
||||
case types.Pointer:
|
||||
@@ -718,11 +861,15 @@ func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type)
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
validationSchema, err := ParseCommentTags(m.Type, m.CommentLines, markerPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.validatePatchTags(m, parent); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("\"$.$\": {\n", name)
|
||||
if err := g.generateMemberExtensions(m, parent); err != nil {
|
||||
if err := g.generateMemberExtensions(m, parent, validationSchema.Extensions); err != nil {
|
||||
return err
|
||||
}
|
||||
g.Do("SchemaProps: spec.SchemaProps{\n", nil)
|
||||
@@ -741,6 +888,10 @@ func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type)
|
||||
if err := g.generateDefault(m.CommentLines, m.Type, omitEmpty, parent); err != nil {
|
||||
return fmt.Errorf("failed to generate default in %v: %v: %v", parent, m.Name, err)
|
||||
}
|
||||
err = g.generateValueValidations(&validationSchema.SchemaProps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t := resolveAliasAndPtrType(m.Type)
|
||||
// If we can get a openAPI type and format for this type, we consider it to be simple property
|
||||
typeString, format := openapi.OpenAPITypeFormat(t.String())
|
||||
@@ -814,6 +965,10 @@ func (g openAPITypeWriter) generateMapProperty(t *types.Type) error {
|
||||
typeString, format := openapi.OpenAPITypeFormat(elemType.String())
|
||||
if typeString != "" {
|
||||
g.generateSimpleProperty(typeString, format)
|
||||
if enumType, isEnum := g.enumContext.EnumType(t.Elem); isEnum {
|
||||
// original type is an enum, add "Enum: " and the values
|
||||
g.Do("Enum: []interface{}{$.$},\n", strings.Join(enumType.ValueStrings(), ", "))
|
||||
}
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
@@ -847,6 +1002,10 @@ func (g openAPITypeWriter) generateSliceProperty(t *types.Type) error {
|
||||
typeString, format := openapi.OpenAPITypeFormat(elemType.String())
|
||||
if typeString != "" {
|
||||
g.generateSimpleProperty(typeString, format)
|
||||
if enumType, isEnum := g.enumContext.EnumType(t.Elem); isEnum {
|
||||
// original type is an enum, add "Enum: " and the values
|
||||
g.Do("Enum: []interface{}{$.$},\n", strings.Join(enumType.ValueStrings(), ", "))
|
||||
}
|
||||
g.Do("},\n},\n},\n", nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
8
vendor/k8s.io/kube-openapi/pkg/generators/rules/idl_tag.go
generated
vendored
8
vendor/k8s.io/kube-openapi/pkg/generators/rules/idl_tag.go
generated
vendored
@@ -1,7 +1,8 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/gengo/v2"
|
||||
"k8s.io/gengo/v2/types"
|
||||
)
|
||||
|
||||
const ListTypeIDLTag = "listType"
|
||||
@@ -24,7 +25,7 @@ func (l *ListTypeMissing) Validate(t *types.Type) ([]string, error) {
|
||||
switch t.Kind {
|
||||
case types.Struct:
|
||||
for _, m := range t.Members {
|
||||
hasListType := types.ExtractCommentTags("+", m.CommentLines)[ListTypeIDLTag] != nil
|
||||
hasListType := gengo.ExtractCommentTags("+", m.CommentLines)[ListTypeIDLTag] != nil
|
||||
|
||||
if m.Name == "Items" && m.Type.Kind == types.Slice && hasNamedMember(t, "ListMeta") {
|
||||
if hasListType {
|
||||
@@ -33,7 +34,8 @@ func (l *ListTypeMissing) Validate(t *types.Type) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Type.Kind == types.Slice && !hasListType {
|
||||
// All slice fields must have a list-type tag except []byte
|
||||
if m.Type.Kind == types.Slice && m.Type.Elem != types.Byte && !hasListType {
|
||||
fields = append(fields, m.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
4
vendor/k8s.io/kube-openapi/pkg/generators/rules/names_match.go
generated
vendored
4
vendor/k8s.io/kube-openapi/pkg/generators/rules/names_match.go
generated
vendored
@@ -22,7 +22,7 @@ import (
|
||||
|
||||
"k8s.io/kube-openapi/pkg/util/sets"
|
||||
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/gengo/v2/types"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -135,7 +135,7 @@ func namesMatch(goName, jsonName string) bool {
|
||||
if !isAllowedName(goName) || !isAllowedName(jsonName) {
|
||||
return false
|
||||
}
|
||||
if strings.ToLower(goName) != strings.ToLower(jsonName) {
|
||||
if !strings.EqualFold(goName, jsonName) {
|
||||
return false
|
||||
}
|
||||
// Go field names must be CamelCase. JSON field names must be camelCase.
|
||||
|
||||
2
vendor/k8s.io/kube-openapi/pkg/generators/rules/omitempty_match_case.go
generated
vendored
2
vendor/k8s.io/kube-openapi/pkg/generators/rules/omitempty_match_case.go
generated
vendored
@@ -20,7 +20,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/gengo/v2/types"
|
||||
)
|
||||
|
||||
// OmitEmptyMatchCase implements APIRule interface.
|
||||
|
||||
19
vendor/k8s.io/kube-openapi/pkg/generators/union.go
generated
vendored
19
vendor/k8s.io/kube-openapi/pkg/generators/union.go
generated
vendored
@@ -20,7 +20,8 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"k8s.io/gengo/types"
|
||||
"k8s.io/gengo/v2"
|
||||
"k8s.io/gengo/v2/types"
|
||||
)
|
||||
|
||||
const tagUnionMember = "union"
|
||||
@@ -141,7 +142,7 @@ func parseEmbeddedUnion(t *types.Type) ([]union, []error) {
|
||||
// embedded types.
|
||||
func parseUnionStruct(t *types.Type) (*union, []error) {
|
||||
errors := []error{}
|
||||
if types.ExtractCommentTags("+", t.CommentLines)[tagUnionMember] == nil {
|
||||
if gengo.ExtractCommentTags("+", t.CommentLines)[tagUnionMember] == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -156,14 +157,14 @@ func parseUnionStruct(t *types.Type) (*union, []error) {
|
||||
errors = append(errors, fmt.Errorf("union structures can't have embedded fields: %v.%v", t.Name, m.Name))
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDeprecated] != nil {
|
||||
if gengo.ExtractCommentTags("+", m.CommentLines)[tagUnionDeprecated] != nil {
|
||||
errors = append(errors, fmt.Errorf("union struct can't have unionDeprecated members: %v.%v", t.Name, m.Name))
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil {
|
||||
if gengo.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil {
|
||||
errors = append(errors, u.setDiscriminator(jsonName)...)
|
||||
} else {
|
||||
if !hasOptionalTag(&m) {
|
||||
if optional, err := isOptional(&m); !optional || err != nil {
|
||||
errors = append(errors, fmt.Errorf("union members must be optional: %v.%v", t.Name, m.Name))
|
||||
}
|
||||
u.addMember(jsonName, m.Name)
|
||||
@@ -186,15 +187,15 @@ func parseUnionMembers(t *types.Type) (*union, []error) {
|
||||
if shouldInlineMembers(&m) {
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil {
|
||||
if gengo.ExtractCommentTags("+", m.CommentLines)[tagUnionDiscriminator] != nil {
|
||||
errors = append(errors, u.setDiscriminator(jsonName)...)
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionMember] != nil {
|
||||
if gengo.ExtractCommentTags("+", m.CommentLines)[tagUnionMember] != nil {
|
||||
errors = append(errors, fmt.Errorf("union tag is not accepted on struct members: %v.%v", t.Name, m.Name))
|
||||
continue
|
||||
}
|
||||
if types.ExtractCommentTags("+", m.CommentLines)[tagUnionDeprecated] != nil {
|
||||
if !hasOptionalTag(&m) {
|
||||
if gengo.ExtractCommentTags("+", m.CommentLines)[tagUnionDeprecated] != nil {
|
||||
if optional, err := isOptional(&m); !optional || err != nil {
|
||||
errors = append(errors, fmt.Errorf("union members must be optional: %v.%v", t.Name, m.Name))
|
||||
}
|
||||
u.addMember(jsonName, m.Name)
|
||||
|
||||
3
vendor/k8s.io/kube-openapi/pkg/schemaconv/smd.go
generated
vendored
3
vendor/k8s.io/kube-openapi/pkg/schemaconv/smd.go
generated
vendored
@@ -214,9 +214,6 @@ func makeUnion(extensions map[string]interface{}) (schema.Union, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if union.Discriminator != nil && len(union.Fields) == 0 {
|
||||
return schema.Union{}, fmt.Errorf("discriminator set to %v, but no fields in union", *union.Discriminator)
|
||||
}
|
||||
return union, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user