1
16
17 package generators
18
19 import (
20 "fmt"
21 "sort"
22 "strings"
23
24 "k8s.io/gengo/v2"
25 "k8s.io/gengo/v2/types"
26 "k8s.io/kube-openapi/pkg/util/sets"
27 )
28
29 const extensionPrefix = "x-kubernetes-"
30
31
32 type extensionAttributes struct {
33 xName string
34 kind types.Kind
35 allowedValues sets.String
36 enforceArray bool
37 }
38
39
40 var tagToExtension = map[string]extensionAttributes{
41 "patchMergeKey": {
42 xName: "x-kubernetes-patch-merge-key",
43 kind: types.Slice,
44 },
45 "patchStrategy": {
46 xName: "x-kubernetes-patch-strategy",
47 kind: types.Slice,
48 allowedValues: sets.NewString("merge", "retainKeys"),
49 },
50 "listMapKey": {
51 xName: "x-kubernetes-list-map-keys",
52 kind: types.Slice,
53 enforceArray: true,
54 },
55 "listType": {
56 xName: "x-kubernetes-list-type",
57 kind: types.Slice,
58 allowedValues: sets.NewString("atomic", "set", "map"),
59 },
60 "mapType": {
61 xName: "x-kubernetes-map-type",
62 kind: types.Map,
63 allowedValues: sets.NewString("atomic", "granular"),
64 },
65 "structType": {
66 xName: "x-kubernetes-map-type",
67 kind: types.Struct,
68 allowedValues: sets.NewString("atomic", "granular"),
69 },
70 "validations": {
71 xName: "x-kubernetes-validations",
72 kind: types.Slice,
73 },
74 }
75
76
77 type extension struct {
78 idlTag string
79 xName string
80 values []string
81 }
82
83 func (e extension) hasAllowedValues() bool {
84 return tagToExtension[e.idlTag].allowedValues.Len() > 0
85 }
86
87 func (e extension) allowedValues() sets.String {
88 return tagToExtension[e.idlTag].allowedValues
89 }
90
91 func (e extension) hasKind() bool {
92 return len(tagToExtension[e.idlTag].kind) > 0
93 }
94
95 func (e extension) kind() types.Kind {
96 return tagToExtension[e.idlTag].kind
97 }
98
99 func (e extension) validateAllowedValues() error {
100
101 if !e.hasAllowedValues() {
102 return nil
103 }
104
105 if len(e.values) == 0 {
106 return fmt.Errorf("%s needs a value, none given.", e.idlTag)
107 }
108
109 allowedValues := e.allowedValues()
110 if !allowedValues.HasAll(e.values...) {
111 return fmt.Errorf("%v not allowed for %s. Allowed values: %v",
112 e.values, e.idlTag, allowedValues.List())
113 }
114 return nil
115 }
116
117 func (e extension) validateType(kind types.Kind) error {
118
119 if !e.hasKind() {
120 return nil
121 }
122 if kind != e.kind() {
123 return fmt.Errorf("tag %s on type %v; only allowed on type %v",
124 e.idlTag, kind, e.kind())
125 }
126 return nil
127 }
128
129 func (e extension) hasMultipleValues() bool {
130 return len(e.values) > 1
131 }
132
133 func (e extension) isAlwaysArrayFormat() bool {
134 return tagToExtension[e.idlTag].enforceArray
135 }
136
137
138 func sortedMapKeys(m map[string][]string) []string {
139 keys := make([]string, len(m))
140 i := 0
141 for k := range m {
142 keys[i] = k
143 i++
144 }
145 sort.Strings(keys)
146 return keys
147 }
148
149
150
151
152
153 func parseExtensions(comments []string) ([]extension, []error) {
154 extensions := []extension{}
155 errors := []error{}
156
157 values := getOpenAPITagValue(comments)
158 for _, val := range values {
159
160 if strings.HasPrefix(val, extensionPrefix) {
161 parts := strings.SplitN(val, ":", 2)
162 if len(parts) != 2 {
163 errors = append(errors, fmt.Errorf("invalid extension value: %v", val))
164 continue
165 }
166 e := extension{
167 idlTag: tagName,
168 xName: parts[0],
169 values: []string{parts[1]},
170 }
171 extensions = append(extensions, e)
172 }
173 }
174
175 tagValues := gengo.ExtractCommentTags("+", comments)
176 for _, idlTag := range sortedMapKeys(tagValues) {
177 xAttrs, exists := tagToExtension[idlTag]
178 if !exists {
179 continue
180 }
181 values := tagValues[idlTag]
182 e := extension{
183 idlTag: idlTag,
184 xName: xAttrs.xName,
185 values: values,
186 }
187 extensions = append(extensions, e)
188 }
189 return extensions, errors
190 }
191
192 func validateMemberExtensions(extensions []extension, m *types.Member) []error {
193 errors := []error{}
194 for _, e := range extensions {
195 if err := e.validateAllowedValues(); err != nil {
196 errors = append(errors, err)
197 }
198 if err := e.validateType(m.Type.Kind); err != nil {
199 errors = append(errors, err)
200 }
201 }
202 return errors
203 }
204
View as plain text