use traditional controller tool to generate code
This commit is contained in:
639
vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go
generated
vendored
Normal file
639
vendor/sigs.k8s.io/controller-tools/pkg/internal/codegen/parse/crd.go
generated
vendored
Normal file
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
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 parse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/gengo/types"
|
||||
)
|
||||
|
||||
// parseCRDs populates the CRD field of each Group.Version.Resource,
|
||||
// creating validations using the annotations on type fields.
|
||||
func (b *APIs) parseCRDs() {
|
||||
for _, group := range b.APIs.Groups {
|
||||
for _, version := range group.Versions {
|
||||
for _, resource := range version.Resources {
|
||||
if IsAPIResource(resource.Type) {
|
||||
resource.JSONSchemaProps, resource.Validation =
|
||||
b.typeToJSONSchemaProps(resource.Type, sets.NewString(), []string{}, true)
|
||||
|
||||
// Note: Drop the Type field at the root level of validation
|
||||
// schema. Refer to following issue for details.
|
||||
// https://github.com/kubernetes/kubernetes/issues/65293
|
||||
resource.JSONSchemaProps.Type = ""
|
||||
j, err := json.MarshalIndent(resource.JSONSchemaProps, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not Marshall validation %v\n", err)
|
||||
}
|
||||
resource.ValidationComments = string(j)
|
||||
|
||||
resource.CRD = v1beta1.CustomResourceDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "apiextensions.k8s.io/v1beta1",
|
||||
Kind: "CustomResourceDefinition",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s.%s.%s", resource.Resource, resource.Group, resource.Domain),
|
||||
Labels: map[string]string{"controller-tools.k8s.io": "1.0"},
|
||||
},
|
||||
Spec: v1beta1.CustomResourceDefinitionSpec{
|
||||
Group: fmt.Sprintf("%s.%s", resource.Group, resource.Domain),
|
||||
Version: resource.Version,
|
||||
Names: v1beta1.CustomResourceDefinitionNames{
|
||||
Kind: resource.Kind,
|
||||
Plural: resource.Resource,
|
||||
},
|
||||
Validation: &v1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &resource.JSONSchemaProps,
|
||||
},
|
||||
},
|
||||
}
|
||||
if resource.NonNamespaced {
|
||||
resource.CRD.Spec.Scope = "Cluster"
|
||||
} else {
|
||||
resource.CRD.Spec.Scope = "Namespaced"
|
||||
}
|
||||
|
||||
if hasCategories(resource.Type) {
|
||||
categoriesTag := getCategoriesTag(resource.Type)
|
||||
categories := strings.Split(categoriesTag, ",")
|
||||
resource.CRD.Spec.Names.Categories = categories
|
||||
resource.Categories = categories
|
||||
}
|
||||
|
||||
if hasSingular(resource.Type) {
|
||||
singularName := getSingularName(resource.Type)
|
||||
resource.CRD.Spec.Names.Singular = singularName
|
||||
}
|
||||
|
||||
if hasStatusSubresource(resource.Type) {
|
||||
if resource.CRD.Spec.Subresources == nil {
|
||||
resource.CRD.Spec.Subresources = &v1beta1.CustomResourceSubresources{}
|
||||
}
|
||||
resource.CRD.Spec.Subresources.Status = &v1beta1.CustomResourceSubresourceStatus{}
|
||||
}
|
||||
|
||||
resource.CRD.Status.Conditions = []v1beta1.CustomResourceDefinitionCondition{}
|
||||
resource.CRD.Status.StoredVersions = []string{}
|
||||
|
||||
if hasScaleSubresource(resource.Type) {
|
||||
if resource.CRD.Spec.Subresources == nil {
|
||||
resource.CRD.Spec.Subresources = &v1beta1.CustomResourceSubresources{}
|
||||
}
|
||||
jsonPath, err := parseScaleParams(resource.Type)
|
||||
if err != nil {
|
||||
log.Fatalf("failed in parsing CRD, error: %v", err.Error())
|
||||
}
|
||||
resource.CRD.Spec.Subresources.Scale = &v1beta1.CustomResourceSubresourceScale{
|
||||
SpecReplicasPath: jsonPath[specReplicasPath],
|
||||
StatusReplicasPath: jsonPath[statusReplicasPath],
|
||||
}
|
||||
labelSelctor, ok := jsonPath[labelSelectorPath]
|
||||
if ok && labelSelctor != "" {
|
||||
resource.CRD.Spec.Subresources.Scale.LabelSelectorPath = &labelSelctor
|
||||
}
|
||||
}
|
||||
if hasPrintColumn(resource.Type) {
|
||||
result, err := parsePrintColumnParams(resource.Type)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse printcolumn annotations, error: %v", err.Error())
|
||||
}
|
||||
resource.CRD.Spec.AdditionalPrinterColumns = result
|
||||
}
|
||||
if len(resource.ShortName) > 0 {
|
||||
resource.CRD.Spec.Names.ShortNames = strings.Split(resource.ShortName, ";")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *APIs) getTime() string {
|
||||
return `v1beta1.JSONSchemaProps{
|
||||
Type: "string",
|
||||
Format: "date-time",
|
||||
}`
|
||||
}
|
||||
|
||||
func (b *APIs) getDuration() string {
|
||||
return `v1beta1.JSONSchemaProps{
|
||||
Type: "string",
|
||||
}`
|
||||
}
|
||||
|
||||
func (b *APIs) getQuantity() string {
|
||||
return `v1beta1.JSONSchemaProps{
|
||||
Type: "string",
|
||||
}`
|
||||
}
|
||||
|
||||
func (b *APIs) objSchema() string {
|
||||
return `v1beta1.JSONSchemaProps{
|
||||
Type: "object",
|
||||
}`
|
||||
}
|
||||
|
||||
// typeToJSONSchemaProps returns a JSONSchemaProps object and its serialization
|
||||
// in Go that describe the JSONSchema validations for the given type.
|
||||
func (b *APIs) typeToJSONSchemaProps(t *types.Type, found sets.String, comments []string, isRoot bool) (v1beta1.JSONSchemaProps, string) {
|
||||
// Special cases
|
||||
time := types.Name{Name: "Time", Package: "k8s.io/apimachinery/pkg/apis/meta/v1"}
|
||||
duration := types.Name{Name: "Duration", Package: "k8s.io/apimachinery/pkg/apis/meta/v1"}
|
||||
quantity := types.Name{Name: "Quantity", Package: "k8s.io/apimachinery/pkg/api/resource"}
|
||||
meta := types.Name{Name: "ObjectMeta", Package: "k8s.io/apimachinery/pkg/apis/meta/v1"}
|
||||
unstructured := types.Name{Name: "Unstructured", Package: "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"}
|
||||
rawExtension := types.Name{Name: "RawExtension", Package: "k8s.io/apimachinery/pkg/runtime"}
|
||||
intOrString := types.Name{Name: "IntOrString", Package: "k8s.io/apimachinery/pkg/util/intstr"}
|
||||
// special types first
|
||||
specialTypeProps := v1beta1.JSONSchemaProps{
|
||||
Description: parseDescription(comments),
|
||||
}
|
||||
for _, l := range comments {
|
||||
getValidation(l, &specialTypeProps)
|
||||
}
|
||||
switch t.Name {
|
||||
case time:
|
||||
specialTypeProps.Type = "string"
|
||||
specialTypeProps.Format = "date-time"
|
||||
return specialTypeProps, b.getTime()
|
||||
case duration:
|
||||
specialTypeProps.Type = "string"
|
||||
return specialTypeProps, b.getDuration()
|
||||
case quantity:
|
||||
specialTypeProps.Type = "string"
|
||||
return specialTypeProps, b.getQuantity()
|
||||
case meta, unstructured, rawExtension:
|
||||
specialTypeProps.Type = "object"
|
||||
return specialTypeProps, b.objSchema()
|
||||
case intOrString:
|
||||
specialTypeProps.AnyOf = []v1beta1.JSONSchemaProps{
|
||||
{
|
||||
Type: "string",
|
||||
},
|
||||
{
|
||||
Type: "integer",
|
||||
},
|
||||
}
|
||||
return specialTypeProps, b.objSchema()
|
||||
}
|
||||
|
||||
var v v1beta1.JSONSchemaProps
|
||||
var s string
|
||||
switch t.Kind {
|
||||
case types.Builtin:
|
||||
v, s = b.parsePrimitiveValidation(t, found, comments)
|
||||
case types.Struct:
|
||||
v, s = b.parseObjectValidation(t, found, comments, isRoot)
|
||||
case types.Map:
|
||||
v, s = b.parseMapValidation(t, found, comments)
|
||||
case types.Slice:
|
||||
v, s = b.parseArrayValidation(t, found, comments)
|
||||
case types.Array:
|
||||
v, s = b.parseArrayValidation(t, found, comments)
|
||||
case types.Pointer:
|
||||
v, s = b.typeToJSONSchemaProps(t.Elem, found, comments, false)
|
||||
case types.Alias:
|
||||
v, s = b.typeToJSONSchemaProps(t.Underlying, found, comments, false)
|
||||
default:
|
||||
log.Fatalf("Unknown supported Kind %v\n", t.Kind)
|
||||
}
|
||||
|
||||
return v, s
|
||||
}
|
||||
|
||||
var jsonRegex = regexp.MustCompile("json:\"([a-zA-Z0-9,]+)\"")
|
||||
|
||||
type primitiveTemplateArgs struct {
|
||||
v1beta1.JSONSchemaProps
|
||||
Value string
|
||||
Format string
|
||||
EnumValue string // TODO check type of enum value to match the type of field
|
||||
Description string
|
||||
}
|
||||
|
||||
var primitiveTemplate = template.Must(template.New("map-template").Parse(
|
||||
`v1beta1.JSONSchemaProps{
|
||||
{{ if .Pattern -}}
|
||||
Pattern: "{{ .Pattern }}",
|
||||
{{ end -}}
|
||||
{{ if .Maximum -}}
|
||||
Maximum: getFloat({{ .Maximum }}),
|
||||
{{ end -}}
|
||||
{{ if .ExclusiveMaximum -}}
|
||||
ExclusiveMaximum: {{ .ExclusiveMaximum }},
|
||||
{{ end -}}
|
||||
{{ if .Minimum -}}
|
||||
Minimum: getFloat({{ .Minimum }}),
|
||||
{{ end -}}
|
||||
{{ if .ExclusiveMinimum -}}
|
||||
ExclusiveMinimum: {{ .ExclusiveMinimum }},
|
||||
{{ end -}}
|
||||
Type: "{{ .Value }}",
|
||||
{{ if .Format -}}
|
||||
Format: "{{ .Format }}",
|
||||
{{ end -}}
|
||||
{{ if .EnumValue -}}
|
||||
Enum: {{ .EnumValue }},
|
||||
{{ end -}}
|
||||
{{ if .MaxLength -}}
|
||||
MaxLength: getInt({{ .MaxLength }}),
|
||||
{{ end -}}
|
||||
{{ if .MinLength -}}
|
||||
MinLength: getInt({{ .MinLength }}),
|
||||
{{ end -}}
|
||||
}`))
|
||||
|
||||
// parsePrimitiveValidation returns a JSONSchemaProps object and its
|
||||
// serialization in Go that describe the validations for the given primitive
|
||||
// type.
|
||||
func (b *APIs) parsePrimitiveValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) {
|
||||
props := v1beta1.JSONSchemaProps{Type: string(t.Name.Name)}
|
||||
|
||||
for _, l := range comments {
|
||||
getValidation(l, &props)
|
||||
}
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
|
||||
var n, f, s, d string
|
||||
switch t.Name.Name {
|
||||
case "int", "int64", "uint64":
|
||||
n = "integer"
|
||||
f = "int64"
|
||||
case "int32", "uint32":
|
||||
n = "integer"
|
||||
f = "int32"
|
||||
case "float", "float32":
|
||||
n = "number"
|
||||
f = "float"
|
||||
case "float64":
|
||||
n = "number"
|
||||
f = "double"
|
||||
case "bool":
|
||||
n = "boolean"
|
||||
case "string":
|
||||
n = "string"
|
||||
f = props.Format
|
||||
default:
|
||||
n = t.Name.Name
|
||||
}
|
||||
if props.Enum != nil {
|
||||
s = parseEnumToString(props.Enum)
|
||||
}
|
||||
d = parseDescription(comments)
|
||||
if err := primitiveTemplate.Execute(buff, primitiveTemplateArgs{props, n, f, s, d}); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
props.Type = n
|
||||
props.Format = f
|
||||
props.Description = d
|
||||
return props, buff.String()
|
||||
}
|
||||
|
||||
type mapTempateArgs struct {
|
||||
Result string
|
||||
SkipMapValidation bool
|
||||
}
|
||||
|
||||
var mapTemplate = template.Must(template.New("map-template").Parse(
|
||||
`v1beta1.JSONSchemaProps{
|
||||
Type: "object",
|
||||
{{if not .SkipMapValidation}}AdditionalProperties: &v1beta1.JSONSchemaPropsOrBool{
|
||||
Allows: true,
|
||||
Schema: &{{.Result}},
|
||||
},{{end}}
|
||||
}`))
|
||||
|
||||
// parseMapValidation returns a JSONSchemaProps object and its serialization in
|
||||
// Go that describe the validations for the given map type.
|
||||
func (b *APIs) parseMapValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) {
|
||||
additionalProps, result := b.typeToJSONSchemaProps(t.Elem, found, comments, false)
|
||||
additionalProps.Description = ""
|
||||
props := v1beta1.JSONSchemaProps{
|
||||
Type: "object",
|
||||
Description: parseDescription(comments),
|
||||
}
|
||||
parseOption := b.arguments.CustomArgs.(*Options)
|
||||
if !parseOption.SkipMapValidation {
|
||||
props.AdditionalProperties = &v1beta1.JSONSchemaPropsOrBool{
|
||||
Allows: true,
|
||||
Schema: &additionalProps}
|
||||
}
|
||||
|
||||
for _, l := range comments {
|
||||
getValidation(l, &props)
|
||||
}
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
if err := mapTemplate.Execute(buff, mapTempateArgs{Result: result, SkipMapValidation: parseOption.SkipMapValidation}); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
return props, buff.String()
|
||||
}
|
||||
|
||||
var arrayTemplate = template.Must(template.New("array-template").Parse(
|
||||
`v1beta1.JSONSchemaProps{
|
||||
Type: "{{.Type}}",
|
||||
{{ if .Format -}}
|
||||
Format: "{{.Format}}",
|
||||
{{ end -}}
|
||||
{{ if .MaxItems -}}
|
||||
MaxItems: getInt({{ .MaxItems }}),
|
||||
{{ end -}}
|
||||
{{ if .MinItems -}}
|
||||
MinItems: getInt({{ .MinItems }}),
|
||||
{{ end -}}
|
||||
{{ if .UniqueItems -}}
|
||||
UniqueItems: {{ .UniqueItems }},
|
||||
{{ end -}}
|
||||
{{ if .Items -}}
|
||||
Items: &v1beta1.JSONSchemaPropsOrArray{
|
||||
Schema: &{{.ItemsSchema}},
|
||||
},
|
||||
{{ end -}}
|
||||
}`))
|
||||
|
||||
type arrayTemplateArgs struct {
|
||||
v1beta1.JSONSchemaProps
|
||||
ItemsSchema string
|
||||
}
|
||||
|
||||
// parseArrayValidation returns a JSONSchemaProps object and its serialization in
|
||||
// Go that describe the validations for the given array type.
|
||||
func (b *APIs) parseArrayValidation(t *types.Type, found sets.String, comments []string) (v1beta1.JSONSchemaProps, string) {
|
||||
items, result := b.typeToJSONSchemaProps(t.Elem, found, comments, false)
|
||||
items.Description = ""
|
||||
props := v1beta1.JSONSchemaProps{
|
||||
Type: "array",
|
||||
Items: &v1beta1.JSONSchemaPropsOrArray{Schema: &items},
|
||||
Description: parseDescription(comments),
|
||||
}
|
||||
// To represent byte arrays in the generated code, the property of the OpenAPI definition
|
||||
// should have string as its type and byte as its format.
|
||||
if t.Name.Name == "[]byte" {
|
||||
props.Type = "string"
|
||||
props.Format = "byte"
|
||||
props.Items = nil
|
||||
props.Description = parseDescription(comments)
|
||||
}
|
||||
for _, l := range comments {
|
||||
getValidation(l, &props)
|
||||
}
|
||||
if t.Name.Name != "[]byte" {
|
||||
// Except for the byte array special case above, the "format" property
|
||||
// should be applied to the array items and not the array itself.
|
||||
props.Format = ""
|
||||
}
|
||||
buff := &bytes.Buffer{}
|
||||
if err := arrayTemplate.Execute(buff, arrayTemplateArgs{props, result}); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
return props, buff.String()
|
||||
}
|
||||
|
||||
type objectTemplateArgs struct {
|
||||
v1beta1.JSONSchemaProps
|
||||
Fields map[string]string
|
||||
Required []string
|
||||
IsRoot bool
|
||||
}
|
||||
|
||||
var objectTemplate = template.Must(template.New("object-template").Parse(
|
||||
`v1beta1.JSONSchemaProps{
|
||||
{{ if not .IsRoot -}}
|
||||
Type: "object",
|
||||
{{ end -}}
|
||||
Properties: map[string]v1beta1.JSONSchemaProps{
|
||||
{{ range $k, $v := .Fields -}}
|
||||
"{{ $k }}": {{ $v }},
|
||||
{{ end -}}
|
||||
},
|
||||
{{if .Required}}Required: []string{
|
||||
{{ range $k, $v := .Required -}}
|
||||
"{{ $v }}",
|
||||
{{ end -}}
|
||||
},{{ end -}}
|
||||
}`))
|
||||
|
||||
// parseObjectValidation returns a JSONSchemaProps object and its serialization in
|
||||
// Go that describe the validations for the given object type.
|
||||
func (b *APIs) parseObjectValidation(t *types.Type, found sets.String, comments []string, isRoot bool) (v1beta1.JSONSchemaProps, string) {
|
||||
buff := &bytes.Buffer{}
|
||||
props := v1beta1.JSONSchemaProps{
|
||||
Type: "object",
|
||||
Description: parseDescription(comments),
|
||||
}
|
||||
|
||||
for _, l := range comments {
|
||||
getValidation(l, &props)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(t.Name.String(), "k8s.io/api") {
|
||||
if err := objectTemplate.Execute(buff, objectTemplateArgs{props, nil, nil, false}); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
} else {
|
||||
m, result, required := b.getMembers(t, found)
|
||||
props.Properties = m
|
||||
props.Required = required
|
||||
|
||||
if err := objectTemplate.Execute(buff, objectTemplateArgs{props, result, required, isRoot}); err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
return props, buff.String()
|
||||
}
|
||||
|
||||
// getValidation parses the validation tags from the comment and sets the
|
||||
// validation rules on the given JSONSchemaProps.
|
||||
func getValidation(comment string, props *v1beta1.JSONSchemaProps) {
|
||||
comment = strings.TrimLeft(comment, " ")
|
||||
if !strings.HasPrefix(comment, "+kubebuilder:validation:") {
|
||||
return
|
||||
}
|
||||
c := strings.Replace(comment, "+kubebuilder:validation:", "", -1)
|
||||
parts := strings.Split(c, "=")
|
||||
if len(parts) != 2 {
|
||||
log.Fatalf("Expected +kubebuilder:validation:<key>=<value> actual: %s", comment)
|
||||
return
|
||||
}
|
||||
switch parts[0] {
|
||||
case "Maximum":
|
||||
f, err := strconv.ParseFloat(parts[1], 64)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse float from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.Maximum = &f
|
||||
case "ExclusiveMaximum":
|
||||
b, err := strconv.ParseBool(parts[1])
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse bool from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.ExclusiveMaximum = b
|
||||
case "Minimum":
|
||||
f, err := strconv.ParseFloat(parts[1], 64)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse float from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.Minimum = &f
|
||||
case "ExclusiveMinimum":
|
||||
b, err := strconv.ParseBool(parts[1])
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse bool from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.ExclusiveMinimum = b
|
||||
case "MaxLength":
|
||||
i, err := strconv.Atoi(parts[1])
|
||||
v := int64(i)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse int from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.MaxLength = &v
|
||||
case "MinLength":
|
||||
i, err := strconv.Atoi(parts[1])
|
||||
v := int64(i)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse int from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.MinLength = &v
|
||||
case "Pattern":
|
||||
props.Pattern = parts[1]
|
||||
case "MaxItems":
|
||||
if props.Type == "array" {
|
||||
i, err := strconv.Atoi(parts[1])
|
||||
v := int64(i)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse int from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.MaxItems = &v
|
||||
}
|
||||
case "MinItems":
|
||||
if props.Type == "array" {
|
||||
i, err := strconv.Atoi(parts[1])
|
||||
v := int64(i)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse int from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.MinItems = &v
|
||||
}
|
||||
case "UniqueItems":
|
||||
if props.Type == "array" {
|
||||
b, err := strconv.ParseBool(parts[1])
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse bool from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.UniqueItems = b
|
||||
}
|
||||
case "MultipleOf":
|
||||
f, err := strconv.ParseFloat(parts[1], 64)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not parse float from %s: %v", comment, err)
|
||||
return
|
||||
}
|
||||
props.MultipleOf = &f
|
||||
case "Enum":
|
||||
if props.Type != "array" {
|
||||
value := strings.Split(parts[1], ",")
|
||||
enums := []v1beta1.JSON{}
|
||||
for _, s := range value {
|
||||
checkType(props, s, &enums)
|
||||
}
|
||||
props.Enum = enums
|
||||
}
|
||||
case "Format":
|
||||
props.Format = parts[1]
|
||||
default:
|
||||
log.Fatalf("Unsupport validation: %s", comment)
|
||||
}
|
||||
}
|
||||
|
||||
// getMembers builds maps by field name of the JSONSchemaProps and their Go
|
||||
// serializations.
|
||||
func (b *APIs) getMembers(t *types.Type, found sets.String) (map[string]v1beta1.JSONSchemaProps, map[string]string, []string) {
|
||||
members := map[string]v1beta1.JSONSchemaProps{}
|
||||
result := map[string]string{}
|
||||
required := []string{}
|
||||
|
||||
// Don't allow recursion until we support it through refs
|
||||
// TODO: Support recursion
|
||||
if found.Has(t.Name.String()) {
|
||||
fmt.Printf("Breaking recursion for type %s", t.Name.String())
|
||||
return members, result, required
|
||||
}
|
||||
found.Insert(t.Name.String())
|
||||
|
||||
for _, member := range t.Members {
|
||||
tags := jsonRegex.FindStringSubmatch(member.Tags)
|
||||
if len(tags) == 0 {
|
||||
// Skip fields without json tags
|
||||
//fmt.Printf("Skipping member %s %s\n", member.Name, member.Type.Name.String())
|
||||
continue
|
||||
}
|
||||
ts := strings.Split(tags[1], ",")
|
||||
name := member.Name
|
||||
strat := ""
|
||||
if len(ts) > 0 && len(ts[0]) > 0 {
|
||||
name = ts[0]
|
||||
}
|
||||
if len(ts) > 1 {
|
||||
strat = ts[1]
|
||||
}
|
||||
|
||||
// Inline "inline" structs
|
||||
if strat == "inline" {
|
||||
m, r, re := b.getMembers(member.Type, found)
|
||||
for n, v := range m {
|
||||
members[n] = v
|
||||
}
|
||||
for n, v := range r {
|
||||
result[n] = v
|
||||
}
|
||||
required = append(required, re...)
|
||||
} else {
|
||||
m, r := b.typeToJSONSchemaProps(member.Type, found, member.CommentLines, false)
|
||||
members[name] = m
|
||||
result[name] = r
|
||||
if !strings.HasSuffix(strat, "omitempty") {
|
||||
required = append(required, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defer found.Delete(t.Name.String())
|
||||
return members, result, required
|
||||
}
|
||||
Reference in New Issue
Block a user