bump(k8s.io/kube-openapi): a07b7bbb58e7fdc5144f8d7046331d29fc9ad3b3

This commit is contained in:
Eric Chiang
2018-01-10 15:17:37 -08:00
parent eb0ac60175
commit e1dda7e3be
16 changed files with 165 additions and 460 deletions

View File

@@ -11,5 +11,36 @@ escape or quote the value string. Extensions can be used to pass more informatio
documentation generators. For example a type might have a friendly name to be displayed in documentation or
being used in a client's fluent interface.
# Custom OpenAPI type definitions
Custom types which otherwise don't map directly to OpenAPI can override their
OpenAPI definition by implementing a function named "OpenAPIDefinition" with
the following signature:
import openapi "k8s.io/kube-openapi/pkg/common"
// ...
type Time struct {
time.Time
}
func (_ Time) OpenAPIDefinition() openapi.OpenAPIDefinition {
return openapi.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "date-time",
},
},
}
}
Alternatively, the type can avoid the "openapi" import by defining the following
methods. The following example produces the same OpenAPI definition as the
example above:
func (_ Time) OpenAPISchemaType() []string { return []string{"string"} }
func (_ Time) OpenAPISchemaFormat() string { return "date-time" }
TODO(mehdy): Make k8s:openapi-gen a parameter to the generator now that OpenAPI has its own repo.

View File

@@ -118,35 +118,13 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
`)...)
outputPath := arguments.OutputPackagePath
if err := context.AddDir(outputPath); err != nil {
glog.Fatalf("Failed to load output package: %v", err)
}
// Compute the canonical output path to allow retrieval of the
// package for a vendored output path.
const vendorPath = "/vendor/"
canonicalOutputPath := outputPath
if strings.Contains(outputPath, vendorPath) {
canonicalOutputPath = outputPath[strings.Index(outputPath, vendorPath)+len(vendorPath):]
}
// The package for outputPath is mapped to the canonical path
pkg := context.Universe[canonicalOutputPath]
if pkg == nil {
glog.Fatalf("Got nil output package: %v", err)
}
return generator.Packages{
&generator.DefaultPackage{
PackageName: strings.Split(filepath.Base(pkg.Path), ".")[0],
// Use the supplied output path rather than the canonical
// one to allow generation into the path of a
// vendored package.
PackagePath: outputPath,
PackageName: filepath.Base(arguments.OutputPackagePath),
PackagePath: arguments.OutputPackagePath,
HeaderText: header,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
return []generator.Generator{NewOpenAPIGen(arguments.OutputFileBaseName, pkg, context)}
return []generator.Generator{NewOpenAPIGen(arguments.OutputFileBaseName, arguments.OutputPackagePath, context)}
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
// There is a conflict between this codegen and codecgen, we should avoid types generated for codecgen
@@ -175,12 +153,12 @@ const (
type openAPIGen struct {
generator.DefaultGen
// TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions.
targetPackage *types.Package
targetPackage string
imports namer.ImportTracker
context *generator.Context
}
func NewOpenAPIGen(sanitizedName string, targetPackage *types.Package, context *generator.Context) generator.Generator {
func NewOpenAPIGen(sanitizedName string, targetPackage string, context *generator.Context) generator.Generator {
return &openAPIGen{
DefaultGen: generator.DefaultGen{
OptionalName: sanitizedName,
@@ -194,7 +172,7 @@ func NewOpenAPIGen(sanitizedName string, targetPackage *types.Package, context *
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.Path, g.imports),
"raw": namer.NewRawNamer(g.targetPackage, g.imports),
}
}
@@ -207,10 +185,10 @@ func (g *openAPIGen) Filter(c *generator.Context, t *types.Type) bool {
}
func (g *openAPIGen) isOtherPackage(pkg string) bool {
if pkg == g.targetPackage.Path {
if pkg == g.targetPackage {
return false
}
if strings.HasSuffix(pkg, "\""+g.targetPackage.Path+"\"") {
if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
return false
}
return true
@@ -300,23 +278,37 @@ func newOpenAPITypeWriter(sw *generator.SnippetWriter) openAPITypeWriter {
}
}
func methodReturnsValue(mt *types.Type, pkg, name string) bool {
if len(mt.Signature.Parameters) != 0 || len(mt.Signature.Results) != 1 {
return false
}
r := mt.Signature.Results[0]
return r.Name.Name == name && r.Name.Package == pkg
}
func hasOpenAPIDefinitionMethod(t *types.Type) bool {
for mn, mt := range t.Methods {
if mn != "OpenAPIDefinition" {
continue
}
if len(mt.Signature.Parameters) != 0 || len(mt.Signature.Results) != 1 {
return false
}
r := mt.Signature.Results[0]
if r.Name.Name != "OpenAPIDefinition" || r.Name.Package != openAPICommonPackagePath {
return false
}
return true
return methodReturnsValue(mt, openAPICommonPackagePath, "OpenAPIDefinition")
}
return false
}
func hasOpenAPIDefinitionMethods(t *types.Type) bool {
var hasSchemaTypeMethod, hasOpenAPISchemaFormat bool
for mn, mt := range t.Methods {
switch mn {
case "OpenAPISchemaType":
hasSchemaTypeMethod = methodReturnsValue(mt, "", "[]string")
case "OpenAPISchemaFormat":
hasOpenAPISchemaFormat = methodReturnsValue(mt, "", "string")
}
}
return hasSchemaTypeMethod && hasOpenAPISchemaFormat
}
// 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
@@ -360,6 +352,28 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
g.Do("$.type|raw${}.OpenAPIDefinition(),\n", args)
return nil
}
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"+
"Schema: spec.Schema{\n"+
"SchemaProps: spec.SchemaProps{\n"+
"Type:$.type|raw${}.OpenAPISchemaType(),\n"+
"Format:$.type|raw${}.OpenAPISchemaFormat(),\n"+
"},\n"+
"},\n"+
"},\n", args)
return nil
}
g.Do("{\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
g.generateDescription(t.CommentLines)
g.Do("Properties: map[string]$.SpecSchemaType|raw${\n", args)

View File

@@ -210,11 +210,18 @@ func (d *Definitions) parseKind(s *openapi_v2.Schema, path *Path) (Schema, error
}, nil
}
func (d *Definitions) parseArbitrary(s *openapi_v2.Schema, path *Path) (Schema, error) {
return &Arbitrary{
BaseSchema: d.parseBaseSchema(s, path),
}, nil
}
// ParseSchema creates a walkable Schema from an openapi schema. While
// this function is public, it doesn't leak through the interface.
func (d *Definitions) ParseSchema(s *openapi_v2.Schema, path *Path) (Schema, error) {
if len(s.GetType().GetValue()) == 1 {
t := s.GetType().GetValue()[0]
objectTypes := s.GetType().GetValue()
if len(objectTypes) == 1 {
t := objectTypes[0]
switch t {
case object:
return d.parseMap(s, path)
@@ -229,6 +236,9 @@ func (d *Definitions) ParseSchema(s *openapi_v2.Schema, path *Path) (Schema, err
if s.GetProperties() != nil {
return d.parseKind(s, path)
}
if len(objectTypes) == 0 || (len(objectTypes) == 1 && objectTypes[0] == "") {
return d.parseArbitrary(s, path)
}
return d.parsePrimitive(s, path)
}

View File

@@ -58,6 +58,14 @@ type SchemaVisitor interface {
VisitReference(Reference)
}
// SchemaVisitorArbitrary is an additional visitor interface which handles
// arbitrary types. For backwards compatability, it's a separate interface
// which is checked for at runtime.
type SchemaVisitorArbitrary interface {
SchemaVisitor
VisitArbitrary(*Arbitrary)
}
// Schema is the base definition of an openapi type.
type Schema interface {
// Giving a visitor here will let you visit the actual type.
@@ -242,6 +250,23 @@ func (p *Primitive) GetName() string {
return fmt.Sprintf("%s (%s)", p.Type, p.Format)
}
// Arbitrary is a value of any type (primitive, object or array)
type Arbitrary struct {
BaseSchema
}
var _ Schema = &Arbitrary{}
func (a *Arbitrary) Accept(v SchemaVisitor) {
if visitor, ok := v.(SchemaVisitorArbitrary); ok {
visitor.VisitArbitrary(a)
}
}
func (a *Arbitrary) GetName() string {
return "Arbitrary value (primitive, object or array)"
}
// Reference implementation depends on the type of document.
type Reference interface {
Schema

View File

@@ -127,6 +127,9 @@ func (item *mapItem) VisitKind(schema *proto.Kind) {
}
}
func (item *mapItem) VisitArbitrary(schema *proto.Arbitrary) {
}
func (item *mapItem) VisitReference(schema proto.Reference) {
// passthrough
schema.SubSchema().Accept(item)
@@ -163,11 +166,14 @@ func (item *arrayItem) VisitArray(schema *proto.Array) {
}
func (item *arrayItem) VisitMap(schema *proto.Map) {
item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "array", Actual: "map"})
item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "map", Actual: "array"})
}
func (item *arrayItem) VisitKind(schema *proto.Kind) {
item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "array", Actual: "map"})
item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "map", Actual: "array"})
}
func (item *arrayItem) VisitArbitrary(schema *proto.Arbitrary) {
}
func (item *arrayItem) VisitReference(schema proto.Reference) {
@@ -226,6 +232,9 @@ func (item *primitiveItem) VisitKind(schema *proto.Kind) {
item.AddValidationError(InvalidTypeError{Path: schema.GetPath().String(), Expected: "map", Actual: item.Kind})
}
func (item *primitiveItem) VisitArbitrary(schema *proto.Arbitrary) {
}
func (item *primitiveItem) VisitReference(schema proto.Reference) {
// passthrough
schema.SubSchema().Accept(item)