...
1
2
3
4 package fieldmeta
5
6 import (
7 "encoding/json"
8 "fmt"
9 "reflect"
10 "strconv"
11 "strings"
12
13 "k8s.io/kube-openapi/pkg/validation/spec"
14 "sigs.k8s.io/kustomize/kyaml/errors"
15 "sigs.k8s.io/kustomize/kyaml/openapi"
16 "sigs.k8s.io/kustomize/kyaml/yaml"
17 )
18
19
20 type FieldMeta struct {
21 Schema spec.Schema
22
23 Extensions XKustomize
24
25 SettersSchema *spec.Schema
26 }
27
28 type XKustomize struct {
29 SetBy string `yaml:"setBy,omitempty" json:"setBy,omitempty"`
30 PartialFieldSetters []PartialFieldSetter `yaml:"partialSetters,omitempty" json:"partialSetters,omitempty"`
31 FieldSetter *PartialFieldSetter `yaml:"setter,omitempty" json:"setter,omitempty"`
32 }
33
34
35
36 type PartialFieldSetter struct {
37
38 Name string `yaml:"name" json:"name"`
39
40
41 Value string `yaml:"value" json:"value"`
42 }
43
44
45 func (fm *FieldMeta) IsEmpty() bool {
46 if fm == nil {
47 return true
48 }
49 return reflect.DeepEqual(fm.Schema, spec.Schema{})
50 }
51
52
53 func (fm *FieldMeta) Read(n *yaml.RNode) error {
54
55 comments := []string{n.YNode().LineComment, n.YNode().HeadComment}
56 for _, c := range comments {
57 if c == "" {
58 continue
59 }
60 c := strings.TrimLeft(c, "#")
61
62
63 if !fm.processShortHand(c) {
64
65
66
67
68 if err := fm.Schema.UnmarshalJSON([]byte(c)); err != nil {
69
70 return nil
71 }
72 }
73 fe := fm.Schema.VendorExtensible.Extensions["x-kustomize"]
74 if fe == nil {
75 return nil
76 }
77 b, err := json.Marshal(fe)
78 if err != nil {
79 return errors.Wrap(err)
80 }
81 return json.Unmarshal(b, &fm.Extensions)
82 }
83 return nil
84 }
85
86
87
88
89 func (fm *FieldMeta) processShortHand(comment string) bool {
90 input := map[string]string{}
91 err := json.Unmarshal([]byte(comment), &input)
92 if err != nil {
93 return false
94 }
95 name := input[shortHandRef]
96 if name == "" {
97 return false
98 }
99
100
101
102
103 setterRef, err := spec.NewRef(DefinitionsPrefix + SetterDefinitionPrefix + name)
104 if err != nil {
105 return false
106 }
107
108 setterRefBytes, err := setterRef.MarshalJSON()
109 if err != nil {
110 return false
111 }
112
113 if _, err := openapi.Resolve(&setterRef, fm.SettersSchema); err == nil {
114 setterErr := fm.Schema.UnmarshalJSON(setterRefBytes)
115 return setterErr == nil
116 }
117
118 substRef, err := spec.NewRef(DefinitionsPrefix + SubstitutionDefinitionPrefix + name)
119 if err != nil {
120 return false
121 }
122
123 substRefBytes, err := substRef.MarshalJSON()
124 if err != nil {
125 return false
126 }
127
128 if _, err := openapi.Resolve(&substRef, fm.SettersSchema); err == nil {
129 substErr := fm.Schema.UnmarshalJSON(substRefBytes)
130 return substErr == nil
131 }
132 return false
133 }
134
135 func isExtensionEmpty(x XKustomize) bool {
136 if x.FieldSetter != nil {
137 return false
138 }
139 if x.SetBy != "" {
140 return false
141 }
142 if len(x.PartialFieldSetters) > 0 {
143 return false
144 }
145 return true
146 }
147
148
149 func (fm *FieldMeta) Write(n *yaml.RNode) error {
150 if !isExtensionEmpty(fm.Extensions) {
151 return fm.WriteV1Setters(n)
152 }
153
154
155 if fm.Schema.Ref.String() != "" {
156
157
158 ref := fm.Schema.Ref.String()
159 var shortHandRefValue string
160 switch {
161 case strings.HasPrefix(ref, DefinitionsPrefix+SetterDefinitionPrefix):
162 shortHandRefValue = strings.TrimPrefix(ref, DefinitionsPrefix+SetterDefinitionPrefix)
163 case strings.HasPrefix(ref, DefinitionsPrefix+SubstitutionDefinitionPrefix):
164 shortHandRefValue = strings.TrimPrefix(ref, DefinitionsPrefix+SubstitutionDefinitionPrefix)
165 default:
166 return fmt.Errorf("unexpected ref format: %s", ref)
167 }
168 n.YNode().LineComment = fmt.Sprintf(`{"%s":"%s"}`, shortHandRef,
169 shortHandRefValue)
170 } else {
171 n.YNode().LineComment = ""
172 }
173
174 return nil
175 }
176
177
178
179 func (fm *FieldMeta) WriteV1Setters(n *yaml.RNode) error {
180 fm.Schema.VendorExtensible.AddExtension("x-kustomize", fm.Extensions)
181 b, err := json.Marshal(fm.Schema)
182 if err != nil {
183 return errors.Wrap(err)
184 }
185 n.YNode().LineComment = string(b)
186 return nil
187 }
188
189
190 type FieldValueType string
191
192 const (
193
194 String FieldValueType = "string"
195
196 Bool = "boolean"
197
198 Int = "integer"
199 )
200
201 func (it FieldValueType) String() string {
202 if it == "" {
203 return "string"
204 }
205 return string(it)
206 }
207
208 func (it FieldValueType) Validate(value string) error {
209 switch it {
210 case Int:
211 if _, err := strconv.Atoi(value); err != nil {
212 return errors.WrapPrefixf(err, "value must be an int")
213 }
214 case Bool:
215 if _, err := strconv.ParseBool(value); err != nil {
216 return errors.WrapPrefixf(err, "value must be a bool")
217 }
218 }
219 return nil
220 }
221
222 func (it FieldValueType) Tag() string {
223 switch it {
224 case String:
225 return yaml.NodeTagString
226 case Bool:
227 return yaml.NodeTagBool
228 case Int:
229 return yaml.NodeTagInt
230 }
231 return ""
232 }
233
234 func (it FieldValueType) TagForValue(value string) string {
235 switch it {
236 case String:
237 return yaml.NodeTagString
238 case Bool:
239 if _, err := strconv.ParseBool(string(it)); err != nil {
240 return ""
241 }
242 return yaml.NodeTagBool
243 case Int:
244 if _, err := strconv.ParseInt(string(it), 0, 32); err != nil {
245 return ""
246 }
247 return yaml.NodeTagInt
248 }
249 return ""
250 }
251
252 const (
253
254 CLIDefinitionsPrefix = "io.k8s.cli."
255
256
257 SetterDefinitionPrefix = CLIDefinitionsPrefix + "setters."
258
259
260 SubstitutionDefinitionPrefix = CLIDefinitionsPrefix + "substitutions."
261
262
263 DefinitionsPrefix = "#/definitions/"
264 )
265
266
267 var shortHandRef = "$openapi"
268
269 func SetShortHandRef(ref string) {
270 shortHandRef = ref
271 }
272
273 func ShortHandRef() string {
274 return shortHandRef
275 }
276
View as plain text