mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
Add patchMergeKey and patchStrategy support to OpenAPI
This commit is contained in:
parent
27cf62ac29
commit
cac0144911
@ -40,15 +40,30 @@ const tagOptional = "optional"
|
|||||||
|
|
||||||
// Known values for the tag.
|
// Known values for the tag.
|
||||||
const (
|
const (
|
||||||
tagValueTrue = "true"
|
tagValueTrue = "true"
|
||||||
tagValueFalse = "false"
|
tagValueFalse = "false"
|
||||||
tagExtensionPrefix = "x-kubernetes-"
|
tagExtensionPrefix = "x-kubernetes-"
|
||||||
|
tagPatchStrategy = "patchStrategy"
|
||||||
|
tagPatchMergeKey = "patchMergeKey"
|
||||||
|
patchStrategyExtensionName = "patch-strategy"
|
||||||
|
patchMergeKeyExtensionName = "patch-merge-key"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getOpenAPITagValue(comments []string) []string {
|
func getOpenAPITagValue(comments []string) []string {
|
||||||
return types.ExtractCommentTags("+", comments)[tagName]
|
return types.ExtractCommentTags("+", comments)[tagName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSingleTagsValue(comments []string, tag string) (string, error) {
|
||||||
|
tags, ok := types.ExtractCommentTags("+", comments)[tag]
|
||||||
|
if !ok || len(tags) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if len(tags) > 1 {
|
||||||
|
return "", fmt.Errorf("Multiple values are not allowed for tag %s", tag)
|
||||||
|
}
|
||||||
|
return tags[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
func hasOpenAPITagValue(comments []string, value string) bool {
|
func hasOpenAPITagValue(comments []string, value string) bool {
|
||||||
tagValues := getOpenAPITagValue(comments)
|
tagValues := getOpenAPITagValue(comments)
|
||||||
for _, val := range tagValues {
|
for _, val := range tagValues {
|
||||||
@ -235,6 +250,10 @@ func getJsonTags(m *types.Member) []string {
|
|||||||
return strings.Split(jsonTag, ",")
|
return strings.Split(jsonTag, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPatchTags(m *types.Member) (string, string) {
|
||||||
|
return reflect.StructTag(m.Tags).Get(tagPatchMergeKey), reflect.StructTag(m.Tags).Get(tagPatchStrategy)
|
||||||
|
}
|
||||||
|
|
||||||
func getReferableName(m *types.Member) string {
|
func getReferableName(m *types.Member) string {
|
||||||
jsonTags := getJsonTags(m)
|
jsonTags := getJsonTags(m)
|
||||||
if len(jsonTags) > 0 {
|
if len(jsonTags) > 0 {
|
||||||
@ -308,7 +327,7 @@ func (g openAPITypeWriter) generateMembers(t *types.Type, required []string) ([]
|
|||||||
if !hasOptionalTag(&m) {
|
if !hasOptionalTag(&m) {
|
||||||
required = append(required, name)
|
required = append(required, name)
|
||||||
}
|
}
|
||||||
if err = g.generateProperty(&m); err != nil {
|
if err = g.generateProperty(&m, t); err != nil {
|
||||||
return required, err
|
return required, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,23 +383,63 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
|
|||||||
|
|
||||||
func (g openAPITypeWriter) generateExtensions(CommentLines []string) error {
|
func (g openAPITypeWriter) generateExtensions(CommentLines []string) error {
|
||||||
tagValues := getOpenAPITagValue(CommentLines)
|
tagValues := getOpenAPITagValue(CommentLines)
|
||||||
anyExtension := false
|
type NameValue struct {
|
||||||
|
Name, Value string
|
||||||
|
}
|
||||||
|
extensions := []NameValue{}
|
||||||
for _, val := range tagValues {
|
for _, val := range tagValues {
|
||||||
if strings.HasPrefix(val, tagExtensionPrefix) {
|
if strings.HasPrefix(val, tagExtensionPrefix) {
|
||||||
if !anyExtension {
|
|
||||||
g.Do("VendorExtensible: spec.VendorExtensible{\nExtensions: spec.Extensions{\n", nil)
|
|
||||||
anyExtension = true
|
|
||||||
}
|
|
||||||
parts := strings.SplitN(val, ":", 2)
|
parts := strings.SplitN(val, ":", 2)
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return fmt.Errorf("Invalid extension value: %v", val)
|
return fmt.Errorf("Invalid extension value: %v", val)
|
||||||
}
|
}
|
||||||
g.Do("\"$.$\": ", parts[0])
|
extensions = append(extensions, NameValue{parts[0], parts[1]})
|
||||||
g.Do("\"$.$\",\n", parts[1])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if anyExtension {
|
patchMergeKeyTag, err := getSingleTagsValue(CommentLines, tagPatchMergeKey)
|
||||||
g.Do("},\n},\n", nil)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(patchMergeKeyTag) > 0 {
|
||||||
|
extensions = append(extensions, NameValue{tagExtensionPrefix + patchMergeKeyExtensionName, patchMergeKeyTag})
|
||||||
|
}
|
||||||
|
patchStrategyTag, err := getSingleTagsValue(CommentLines, tagPatchStrategy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(patchStrategyTag) > 0 {
|
||||||
|
extensions = append(extensions, NameValue{tagExtensionPrefix + patchStrategyExtensionName, patchStrategyTag})
|
||||||
|
}
|
||||||
|
if len(extensions) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
g.Do("VendorExtensible: spec.VendorExtensible{\nExtensions: spec.Extensions{\n", nil)
|
||||||
|
for _, extension := range extensions {
|
||||||
|
g.Do("\"$.$\": ", extension.Name)
|
||||||
|
g.Do("\"$.$\",\n", extension.Value)
|
||||||
|
}
|
||||||
|
g.Do("},\n},\n", nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#44005): Move this validation outside of this generator (probably to policy verifier)
|
||||||
|
func (g openAPITypeWriter) validatePatchTags(m *types.Member, parent *types.Type) error {
|
||||||
|
patchMergeKeyStructTag, patchStrategyStructTag := getPatchTags(m)
|
||||||
|
patchMergeKeyCommentTag, err := getSingleTagsValue(m.CommentLines, tagPatchMergeKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
patchStrategyCommentTag, err := getSingleTagsValue(m.CommentLines, tagPatchStrategy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if patchMergeKeyStructTag != patchMergeKeyCommentTag {
|
||||||
|
return fmt.Errorf("patchMergeKey in comment and struct tags should match for member (%s) of (%s)",
|
||||||
|
m.Name, parent.Name.String())
|
||||||
|
}
|
||||||
|
if patchStrategyStructTag != patchStrategyCommentTag {
|
||||||
|
return fmt.Errorf("patchStrategy in comment and struct tags should match for member (%s) of (%s)",
|
||||||
|
m.Name, parent.Name.String())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -428,11 +487,14 @@ func (g openAPITypeWriter) generateDescription(CommentLines []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g openAPITypeWriter) generateProperty(m *types.Member) error {
|
func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type) error {
|
||||||
name := getReferableName(m)
|
name := getReferableName(m)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if err := g.validatePatchTags(m, parent); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
g.Do("\"$.$\": {\n", name)
|
g.Do("\"$.$\": {\n", name)
|
||||||
if err := g.generateExtensions(m.CommentLines); err != nil {
|
if err := g.generateExtensions(m.CommentLines); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -111,6 +111,10 @@ type Blah struct {
|
|||||||
// a member with an extension
|
// a member with an extension
|
||||||
// +k8s:openapi-gen=x-kubernetes-member-tag:member_test
|
// +k8s:openapi-gen=x-kubernetes-member-tag:member_test
|
||||||
WithExtension string
|
WithExtension string
|
||||||
|
// a member with struct tag as extension
|
||||||
|
// +patchStrategy=ps
|
||||||
|
// +patchMergeKey=pmk
|
||||||
|
WithStructTagExtension string `+"`"+`patchStrategy:"ps" patchMergeKey:"pmk"`+"`"+`
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -238,8 +242,21 @@ Type: []string{"string"},
|
|||||||
Format: "",
|
Format: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"WithStructTagExtension": {
|
||||||
|
VendorExtensible: spec.VendorExtensible{
|
||||||
|
Extensions: spec.Extensions{
|
||||||
|
"x-kubernetes-patch-merge-key": "pmk",
|
||||||
|
"x-kubernetes-patch-strategy": "ps",
|
||||||
},
|
},
|
||||||
Required: []string{"String","Int64","Int32","Int16","Int8","Uint","Uint64","Uint32","Uint16","Uint8","Byte","Bool","Float64","Float32","ByteArray","WithExtension"},
|
},
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Description: "a member with struct tag as extension",
|
||||||
|
Type: []string{"string"},
|
||||||
|
Format: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Required: []string{"String","Int64","Int32","Int16","Int8","Uint","Uint64","Uint32","Uint16","Uint8","Byte","Bool","Float64","Float32","ByteArray","WithExtension","WithStructTagExtension"},
|
||||||
},
|
},
|
||||||
VendorExtensible: spec.VendorExtensible{
|
VendorExtensible: spec.VendorExtensible{
|
||||||
Extensions: spec.Extensions{
|
Extensions: spec.Extensions{
|
||||||
|
Loading…
Reference in New Issue
Block a user