1
16
17 package unstructured
18
19 import (
20 gojson "encoding/json"
21 "fmt"
22 "io"
23 "strings"
24
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/runtime"
27 "k8s.io/apimachinery/pkg/runtime/schema"
28 "k8s.io/apimachinery/pkg/types"
29 "k8s.io/apimachinery/pkg/util/json"
30 "k8s.io/klog/v2"
31 )
32
33
34
35
36
37
38
39 func NestedFieldCopy(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
40 val, found, err := NestedFieldNoCopy(obj, fields...)
41 if !found || err != nil {
42 return nil, found, err
43 }
44 return runtime.DeepCopyJSONValue(val), true, nil
45 }
46
47
48
49
50
51
52
53 func NestedFieldNoCopy(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
54 var val interface{} = obj
55
56 for i, field := range fields {
57 if val == nil {
58 return nil, false, nil
59 }
60 if m, ok := val.(map[string]interface{}); ok {
61 val, ok = m[field]
62 if !ok {
63 return nil, false, nil
64 }
65 } else {
66 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected map[string]interface{}", jsonPath(fields[:i+1]), val, val)
67 }
68 }
69 return val, true, nil
70 }
71
72
73
74 func NestedString(obj map[string]interface{}, fields ...string) (string, bool, error) {
75 val, found, err := NestedFieldNoCopy(obj, fields...)
76 if !found || err != nil {
77 return "", found, err
78 }
79 s, ok := val.(string)
80 if !ok {
81 return "", false, fmt.Errorf("%v accessor error: %v is of the type %T, expected string", jsonPath(fields), val, val)
82 }
83 return s, true, nil
84 }
85
86
87
88 func NestedBool(obj map[string]interface{}, fields ...string) (bool, bool, error) {
89 val, found, err := NestedFieldNoCopy(obj, fields...)
90 if !found || err != nil {
91 return false, found, err
92 }
93 b, ok := val.(bool)
94 if !ok {
95 return false, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected bool", jsonPath(fields), val, val)
96 }
97 return b, true, nil
98 }
99
100
101
102 func NestedFloat64(obj map[string]interface{}, fields ...string) (float64, bool, error) {
103 val, found, err := NestedFieldNoCopy(obj, fields...)
104 if !found || err != nil {
105 return 0, found, err
106 }
107 f, ok := val.(float64)
108 if !ok {
109 return 0, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected float64", jsonPath(fields), val, val)
110 }
111 return f, true, nil
112 }
113
114
115
116 func NestedInt64(obj map[string]interface{}, fields ...string) (int64, bool, error) {
117 val, found, err := NestedFieldNoCopy(obj, fields...)
118 if !found || err != nil {
119 return 0, found, err
120 }
121 i, ok := val.(int64)
122 if !ok {
123 return 0, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected int64", jsonPath(fields), val, val)
124 }
125 return i, true, nil
126 }
127
128
129
130 func NestedStringSlice(obj map[string]interface{}, fields ...string) ([]string, bool, error) {
131 val, found, err := NestedFieldNoCopy(obj, fields...)
132 if !found || err != nil {
133 return nil, found, err
134 }
135 m, ok := val.([]interface{})
136 if !ok {
137 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected []interface{}", jsonPath(fields), val, val)
138 }
139 strSlice := make([]string, 0, len(m))
140 for _, v := range m {
141 if str, ok := v.(string); ok {
142 strSlice = append(strSlice, str)
143 } else {
144 return nil, false, fmt.Errorf("%v accessor error: contains non-string key in the slice: %v is of the type %T, expected string", jsonPath(fields), v, v)
145 }
146 }
147 return strSlice, true, nil
148 }
149
150
151
152 func NestedSlice(obj map[string]interface{}, fields ...string) ([]interface{}, bool, error) {
153 val, found, err := NestedFieldNoCopy(obj, fields...)
154 if !found || err != nil {
155 return nil, found, err
156 }
157 _, ok := val.([]interface{})
158 if !ok {
159 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected []interface{}", jsonPath(fields), val, val)
160 }
161 return runtime.DeepCopyJSONValue(val).([]interface{}), true, nil
162 }
163
164
165
166 func NestedStringMap(obj map[string]interface{}, fields ...string) (map[string]string, bool, error) {
167 m, found, err := nestedMapNoCopy(obj, fields...)
168 if !found || err != nil {
169 return nil, found, err
170 }
171 strMap := make(map[string]string, len(m))
172 for k, v := range m {
173 if str, ok := v.(string); ok {
174 strMap[k] = str
175 } else {
176 return nil, false, fmt.Errorf("%v accessor error: contains non-string value in the map under key %q: %v is of the type %T, expected string", jsonPath(fields), k, v, v)
177 }
178 }
179 return strMap, true, nil
180 }
181
182
183
184 func NestedMap(obj map[string]interface{}, fields ...string) (map[string]interface{}, bool, error) {
185 m, found, err := nestedMapNoCopy(obj, fields...)
186 if !found || err != nil {
187 return nil, found, err
188 }
189 return runtime.DeepCopyJSON(m), true, nil
190 }
191
192
193
194 func nestedMapNoCopy(obj map[string]interface{}, fields ...string) (map[string]interface{}, bool, error) {
195 val, found, err := NestedFieldNoCopy(obj, fields...)
196 if !found || err != nil {
197 return nil, found, err
198 }
199 m, ok := val.(map[string]interface{})
200 if !ok {
201 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected map[string]interface{}", jsonPath(fields), val, val)
202 }
203 return m, true, nil
204 }
205
206
207
208 func SetNestedField(obj map[string]interface{}, value interface{}, fields ...string) error {
209 return setNestedFieldNoCopy(obj, runtime.DeepCopyJSONValue(value), fields...)
210 }
211
212 func setNestedFieldNoCopy(obj map[string]interface{}, value interface{}, fields ...string) error {
213 m := obj
214
215 for i, field := range fields[:len(fields)-1] {
216 if val, ok := m[field]; ok {
217 if valMap, ok := val.(map[string]interface{}); ok {
218 m = valMap
219 } else {
220 return fmt.Errorf("value cannot be set because %v is not a map[string]interface{}", jsonPath(fields[:i+1]))
221 }
222 } else {
223 newVal := make(map[string]interface{})
224 m[field] = newVal
225 m = newVal
226 }
227 }
228 m[fields[len(fields)-1]] = value
229 return nil
230 }
231
232
233
234 func SetNestedStringSlice(obj map[string]interface{}, value []string, fields ...string) error {
235 m := make([]interface{}, 0, len(value))
236 for _, v := range value {
237 m = append(m, v)
238 }
239 return setNestedFieldNoCopy(obj, m, fields...)
240 }
241
242
243
244 func SetNestedSlice(obj map[string]interface{}, value []interface{}, fields ...string) error {
245 return SetNestedField(obj, value, fields...)
246 }
247
248
249
250 func SetNestedStringMap(obj map[string]interface{}, value map[string]string, fields ...string) error {
251 m := make(map[string]interface{}, len(value))
252 for k, v := range value {
253 m[k] = v
254 }
255 return setNestedFieldNoCopy(obj, m, fields...)
256 }
257
258
259
260 func SetNestedMap(obj map[string]interface{}, value map[string]interface{}, fields ...string) error {
261 return SetNestedField(obj, value, fields...)
262 }
263
264
265 func RemoveNestedField(obj map[string]interface{}, fields ...string) {
266 m := obj
267 for _, field := range fields[:len(fields)-1] {
268 if x, ok := m[field].(map[string]interface{}); ok {
269 m = x
270 } else {
271 return
272 }
273 }
274 delete(m, fields[len(fields)-1])
275 }
276
277 func getNestedString(obj map[string]interface{}, fields ...string) string {
278 val, found, err := NestedString(obj, fields...)
279 if !found || err != nil {
280 return ""
281 }
282 return val
283 }
284
285 func getNestedInt64Pointer(obj map[string]interface{}, fields ...string) *int64 {
286 val, found, err := NestedInt64(obj, fields...)
287 if !found || err != nil {
288 return nil
289 }
290 return &val
291 }
292
293 func jsonPath(fields []string) string {
294 return "." + strings.Join(fields, ".")
295 }
296
297 func extractOwnerReference(v map[string]interface{}) metav1.OwnerReference {
298
299
300 var controllerPtr *bool
301 if controller, found, err := NestedBool(v, "controller"); err == nil && found {
302 controllerPtr = &controller
303 }
304 var blockOwnerDeletionPtr *bool
305 if blockOwnerDeletion, found, err := NestedBool(v, "blockOwnerDeletion"); err == nil && found {
306 blockOwnerDeletionPtr = &blockOwnerDeletion
307 }
308 return metav1.OwnerReference{
309 Kind: getNestedString(v, "kind"),
310 Name: getNestedString(v, "name"),
311 APIVersion: getNestedString(v, "apiVersion"),
312 UID: types.UID(getNestedString(v, "uid")),
313 Controller: controllerPtr,
314 BlockOwnerDeletion: blockOwnerDeletionPtr,
315 }
316 }
317
318
319
320
321 var UnstructuredJSONScheme runtime.Codec = unstructuredJSONScheme{}
322
323 type unstructuredJSONScheme struct{}
324
325 const unstructuredJSONSchemeIdentifier runtime.Identifier = "unstructuredJSON"
326
327 func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
328 var err error
329 if obj != nil {
330 err = s.decodeInto(data, obj)
331 } else {
332 obj, err = s.decode(data)
333 }
334
335 if err != nil {
336 return nil, nil, err
337 }
338
339 gvk := obj.GetObjectKind().GroupVersionKind()
340 if len(gvk.Kind) == 0 {
341 return nil, &gvk, runtime.NewMissingKindErr(string(data))
342 }
343
344
345 return obj, &gvk, nil
346 }
347
348 func (s unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
349 if co, ok := obj.(runtime.CacheableObject); ok {
350 return co.CacheEncode(s.Identifier(), s.doEncode, w)
351 }
352 return s.doEncode(obj, w)
353 }
354
355 func (unstructuredJSONScheme) doEncode(obj runtime.Object, w io.Writer) error {
356 switch t := obj.(type) {
357 case *Unstructured:
358 return json.NewEncoder(w).Encode(t.Object)
359 case *UnstructuredList:
360 items := make([]interface{}, 0, len(t.Items))
361 for _, i := range t.Items {
362 items = append(items, i.Object)
363 }
364 listObj := make(map[string]interface{}, len(t.Object)+1)
365 for k, v := range t.Object {
366 listObj[k] = v
367 }
368 listObj["items"] = items
369 return json.NewEncoder(w).Encode(listObj)
370 case *runtime.Unknown:
371
372 _, err := w.Write(t.Raw)
373 return err
374 default:
375 return json.NewEncoder(w).Encode(t)
376 }
377 }
378
379
380 func (unstructuredJSONScheme) Identifier() runtime.Identifier {
381 return unstructuredJSONSchemeIdentifier
382 }
383
384 func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) {
385 type detector struct {
386 Items gojson.RawMessage `json:"items"`
387 }
388 var det detector
389 if err := json.Unmarshal(data, &det); err != nil {
390 return nil, err
391 }
392
393 if det.Items != nil {
394 list := &UnstructuredList{}
395 err := s.decodeToList(data, list)
396 return list, err
397 }
398
399
400 unstruct := &Unstructured{}
401 err := s.decodeToUnstructured(data, unstruct)
402 return unstruct, err
403 }
404
405 func (s unstructuredJSONScheme) decodeInto(data []byte, obj runtime.Object) error {
406 switch x := obj.(type) {
407 case *Unstructured:
408 return s.decodeToUnstructured(data, x)
409 case *UnstructuredList:
410 return s.decodeToList(data, x)
411 default:
412 return json.Unmarshal(data, x)
413 }
414 }
415
416 func (unstructuredJSONScheme) decodeToUnstructured(data []byte, unstruct *Unstructured) error {
417 m := make(map[string]interface{})
418 if err := json.Unmarshal(data, &m); err != nil {
419 return err
420 }
421
422 unstruct.Object = m
423
424 return nil
425 }
426
427 func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList) error {
428 type decodeList struct {
429 Items []gojson.RawMessage `json:"items"`
430 }
431
432 var dList decodeList
433 if err := json.Unmarshal(data, &dList); err != nil {
434 return err
435 }
436
437 if err := json.Unmarshal(data, &list.Object); err != nil {
438 return err
439 }
440
441
442
443 listAPIVersion := list.GetAPIVersion()
444 listKind := list.GetKind()
445 itemKind := strings.TrimSuffix(listKind, "List")
446
447 delete(list.Object, "items")
448 list.Items = make([]Unstructured, 0, len(dList.Items))
449 for _, i := range dList.Items {
450 unstruct := &Unstructured{}
451 if err := s.decodeToUnstructured([]byte(i), unstruct); err != nil {
452 return err
453 }
454
455
456 if len(unstruct.GetKind()) == 0 && len(unstruct.GetAPIVersion()) == 0 {
457 unstruct.SetKind(itemKind)
458 unstruct.SetAPIVersion(listAPIVersion)
459 }
460 list.Items = append(list.Items, *unstruct)
461 }
462 return nil
463 }
464
465 type jsonFallbackEncoder struct {
466 encoder runtime.Encoder
467 identifier runtime.Identifier
468 }
469
470 func NewJSONFallbackEncoder(encoder runtime.Encoder) runtime.Encoder {
471 result := map[string]string{
472 "name": "fallback",
473 "base": string(encoder.Identifier()),
474 }
475 identifier, err := gojson.Marshal(result)
476 if err != nil {
477 klog.Fatalf("Failed marshaling identifier for jsonFallbackEncoder: %v", err)
478 }
479 return &jsonFallbackEncoder{
480 encoder: encoder,
481 identifier: runtime.Identifier(identifier),
482 }
483 }
484
485 func (c *jsonFallbackEncoder) Encode(obj runtime.Object, w io.Writer) error {
486
487
488 err := c.encoder.Encode(obj, w)
489 if runtime.IsNotRegisteredError(err) {
490 switch obj.(type) {
491 case *Unstructured, *UnstructuredList:
492 return UnstructuredJSONScheme.Encode(obj, w)
493 }
494 }
495 return err
496 }
497
498
499 func (c *jsonFallbackEncoder) Identifier() runtime.Identifier {
500 return c.identifier
501 }
502
View as plain text