blob: 75ac693fe483814286b4ceade68a5003adf37658 [file] [log] [blame]
sslobodrd046be82019-01-16 10:02:22 -05001/*
2Copyright 2015 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package unstructured
18
19import (
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)
31
32// NestedFieldCopy returns a deep copy of the value of a nested field.
33// Returns false if the value is missing.
34// No error is returned for a nil field.
35func NestedFieldCopy(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
36 val, found, err := NestedFieldNoCopy(obj, fields...)
37 if !found || err != nil {
38 return nil, found, err
39 }
40 return runtime.DeepCopyJSONValue(val), true, nil
41}
42
43// NestedFieldNoCopy returns a reference to a nested field.
44// Returns false if value is not found and an error if unable
45// to traverse obj.
46func NestedFieldNoCopy(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
47 var val interface{} = obj
48
49 for i, field := range fields {
50 if val == nil {
51 return nil, false, nil
52 }
53 if m, ok := val.(map[string]interface{}); ok {
54 val, ok = m[field]
55 if !ok {
56 return nil, false, nil
57 }
58 } else {
59 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected map[string]interface{}", jsonPath(fields[:i+1]), val, val)
60 }
61 }
62 return val, true, nil
63}
64
65// NestedString returns the string value of a nested field.
66// Returns false if value is not found and an error if not a string.
67func NestedString(obj map[string]interface{}, fields ...string) (string, bool, error) {
68 val, found, err := NestedFieldNoCopy(obj, fields...)
69 if !found || err != nil {
70 return "", found, err
71 }
72 s, ok := val.(string)
73 if !ok {
74 return "", false, fmt.Errorf("%v accessor error: %v is of the type %T, expected string", jsonPath(fields), val, val)
75 }
76 return s, true, nil
77}
78
79// NestedBool returns the bool value of a nested field.
80// Returns false if value is not found and an error if not a bool.
81func NestedBool(obj map[string]interface{}, fields ...string) (bool, bool, error) {
82 val, found, err := NestedFieldNoCopy(obj, fields...)
83 if !found || err != nil {
84 return false, found, err
85 }
86 b, ok := val.(bool)
87 if !ok {
88 return false, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected bool", jsonPath(fields), val, val)
89 }
90 return b, true, nil
91}
92
93// NestedFloat64 returns the float64 value of a nested field.
94// Returns false if value is not found and an error if not a float64.
95func NestedFloat64(obj map[string]interface{}, fields ...string) (float64, bool, error) {
96 val, found, err := NestedFieldNoCopy(obj, fields...)
97 if !found || err != nil {
98 return 0, found, err
99 }
100 f, ok := val.(float64)
101 if !ok {
102 return 0, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected float64", jsonPath(fields), val, val)
103 }
104 return f, true, nil
105}
106
107// NestedInt64 returns the int64 value of a nested field.
108// Returns false if value is not found and an error if not an int64.
109func NestedInt64(obj map[string]interface{}, fields ...string) (int64, bool, error) {
110 val, found, err := NestedFieldNoCopy(obj, fields...)
111 if !found || err != nil {
112 return 0, found, err
113 }
114 i, ok := val.(int64)
115 if !ok {
116 return 0, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected int64", jsonPath(fields), val, val)
117 }
118 return i, true, nil
119}
120
121// NestedStringSlice returns a copy of []string value of a nested field.
122// Returns false if value is not found and an error if not a []interface{} or contains non-string items in the slice.
123func NestedStringSlice(obj map[string]interface{}, fields ...string) ([]string, bool, error) {
124 val, found, err := NestedFieldNoCopy(obj, fields...)
125 if !found || err != nil {
126 return nil, found, err
127 }
128 m, ok := val.([]interface{})
129 if !ok {
130 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected []interface{}", jsonPath(fields), val, val)
131 }
132 strSlice := make([]string, 0, len(m))
133 for _, v := range m {
134 if str, ok := v.(string); ok {
135 strSlice = append(strSlice, str)
136 } else {
137 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)
138 }
139 }
140 return strSlice, true, nil
141}
142
143// NestedSlice returns a deep copy of []interface{} value of a nested field.
144// Returns false if value is not found and an error if not a []interface{}.
145func NestedSlice(obj map[string]interface{}, fields ...string) ([]interface{}, bool, error) {
146 val, found, err := NestedFieldNoCopy(obj, fields...)
147 if !found || err != nil {
148 return nil, found, err
149 }
150 _, ok := val.([]interface{})
151 if !ok {
152 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected []interface{}", jsonPath(fields), val, val)
153 }
154 return runtime.DeepCopyJSONValue(val).([]interface{}), true, nil
155}
156
157// NestedStringMap returns a copy of map[string]string value of a nested field.
158// Returns false if value is not found and an error if not a map[string]interface{} or contains non-string values in the map.
159func NestedStringMap(obj map[string]interface{}, fields ...string) (map[string]string, bool, error) {
160 m, found, err := nestedMapNoCopy(obj, fields...)
161 if !found || err != nil {
162 return nil, found, err
163 }
164 strMap := make(map[string]string, len(m))
165 for k, v := range m {
166 if str, ok := v.(string); ok {
167 strMap[k] = str
168 } else {
169 return nil, false, fmt.Errorf("%v accessor error: contains non-string key in the map: %v is of the type %T, expected string", jsonPath(fields), v, v)
170 }
171 }
172 return strMap, true, nil
173}
174
175// NestedMap returns a deep copy of map[string]interface{} value of a nested field.
176// Returns false if value is not found and an error if not a map[string]interface{}.
177func NestedMap(obj map[string]interface{}, fields ...string) (map[string]interface{}, bool, error) {
178 m, found, err := nestedMapNoCopy(obj, fields...)
179 if !found || err != nil {
180 return nil, found, err
181 }
182 return runtime.DeepCopyJSON(m), true, nil
183}
184
185// nestedMapNoCopy returns a map[string]interface{} value of a nested field.
186// Returns false if value is not found and an error if not a map[string]interface{}.
187func nestedMapNoCopy(obj map[string]interface{}, fields ...string) (map[string]interface{}, bool, error) {
188 val, found, err := NestedFieldNoCopy(obj, fields...)
189 if !found || err != nil {
190 return nil, found, err
191 }
192 m, ok := val.(map[string]interface{})
193 if !ok {
194 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected map[string]interface{}", jsonPath(fields), val, val)
195 }
196 return m, true, nil
197}
198
199// SetNestedField sets the value of a nested field to a deep copy of the value provided.
200// Returns an error if value cannot be set because one of the nesting levels is not a map[string]interface{}.
201func SetNestedField(obj map[string]interface{}, value interface{}, fields ...string) error {
202 return setNestedFieldNoCopy(obj, runtime.DeepCopyJSONValue(value), fields...)
203}
204
205func setNestedFieldNoCopy(obj map[string]interface{}, value interface{}, fields ...string) error {
206 m := obj
207
208 for i, field := range fields[:len(fields)-1] {
209 if val, ok := m[field]; ok {
210 if valMap, ok := val.(map[string]interface{}); ok {
211 m = valMap
212 } else {
213 return fmt.Errorf("value cannot be set because %v is not a map[string]interface{}", jsonPath(fields[:i+1]))
214 }
215 } else {
216 newVal := make(map[string]interface{})
217 m[field] = newVal
218 m = newVal
219 }
220 }
221 m[fields[len(fields)-1]] = value
222 return nil
223}
224
225// SetNestedStringSlice sets the string slice value of a nested field.
226// Returns an error if value cannot be set because one of the nesting levels is not a map[string]interface{}.
227func SetNestedStringSlice(obj map[string]interface{}, value []string, fields ...string) error {
228 m := make([]interface{}, 0, len(value)) // convert []string into []interface{}
229 for _, v := range value {
230 m = append(m, v)
231 }
232 return setNestedFieldNoCopy(obj, m, fields...)
233}
234
235// SetNestedSlice sets the slice value of a nested field.
236// Returns an error if value cannot be set because one of the nesting levels is not a map[string]interface{}.
237func SetNestedSlice(obj map[string]interface{}, value []interface{}, fields ...string) error {
238 return SetNestedField(obj, value, fields...)
239}
240
241// SetNestedStringMap sets the map[string]string value of a nested field.
242// Returns an error if value cannot be set because one of the nesting levels is not a map[string]interface{}.
243func SetNestedStringMap(obj map[string]interface{}, value map[string]string, fields ...string) error {
244 m := make(map[string]interface{}, len(value)) // convert map[string]string into map[string]interface{}
245 for k, v := range value {
246 m[k] = v
247 }
248 return setNestedFieldNoCopy(obj, m, fields...)
249}
250
251// SetNestedMap sets the map[string]interface{} value of a nested field.
252// Returns an error if value cannot be set because one of the nesting levels is not a map[string]interface{}.
253func SetNestedMap(obj map[string]interface{}, value map[string]interface{}, fields ...string) error {
254 return SetNestedField(obj, value, fields...)
255}
256
257// RemoveNestedField removes the nested field from the obj.
258func RemoveNestedField(obj map[string]interface{}, fields ...string) {
259 m := obj
260 for _, field := range fields[:len(fields)-1] {
261 if x, ok := m[field].(map[string]interface{}); ok {
262 m = x
263 } else {
264 return
265 }
266 }
267 delete(m, fields[len(fields)-1])
268}
269
270func getNestedString(obj map[string]interface{}, fields ...string) string {
271 val, found, err := NestedString(obj, fields...)
272 if !found || err != nil {
273 return ""
274 }
275 return val
276}
277
278func jsonPath(fields []string) string {
279 return "." + strings.Join(fields, ".")
280}
281
282func extractOwnerReference(v map[string]interface{}) metav1.OwnerReference {
283 // though this field is a *bool, but when decoded from JSON, it's
284 // unmarshalled as bool.
285 var controllerPtr *bool
286 if controller, found, err := NestedBool(v, "controller"); err == nil && found {
287 controllerPtr = &controller
288 }
289 var blockOwnerDeletionPtr *bool
290 if blockOwnerDeletion, found, err := NestedBool(v, "blockOwnerDeletion"); err == nil && found {
291 blockOwnerDeletionPtr = &blockOwnerDeletion
292 }
293 return metav1.OwnerReference{
294 Kind: getNestedString(v, "kind"),
295 Name: getNestedString(v, "name"),
296 APIVersion: getNestedString(v, "apiVersion"),
297 UID: types.UID(getNestedString(v, "uid")),
298 Controller: controllerPtr,
299 BlockOwnerDeletion: blockOwnerDeletionPtr,
300 }
301}
302
303// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
304// type, which can be used for generic access to objects without a predefined scheme.
305// TODO: move into serializer/json.
306var UnstructuredJSONScheme runtime.Codec = unstructuredJSONScheme{}
307
308type unstructuredJSONScheme struct{}
309
310func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
311 var err error
312 if obj != nil {
313 err = s.decodeInto(data, obj)
314 } else {
315 obj, err = s.decode(data)
316 }
317
318 if err != nil {
319 return nil, nil, err
320 }
321
322 gvk := obj.GetObjectKind().GroupVersionKind()
323 if len(gvk.Kind) == 0 {
324 return nil, &gvk, runtime.NewMissingKindErr(string(data))
325 }
326
327 return obj, &gvk, nil
328}
329
330func (unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
331 switch t := obj.(type) {
332 case *Unstructured:
333 return json.NewEncoder(w).Encode(t.Object)
334 case *UnstructuredList:
335 items := make([]interface{}, 0, len(t.Items))
336 for _, i := range t.Items {
337 items = append(items, i.Object)
338 }
339 listObj := make(map[string]interface{}, len(t.Object)+1)
340 for k, v := range t.Object { // Make a shallow copy
341 listObj[k] = v
342 }
343 listObj["items"] = items
344 return json.NewEncoder(w).Encode(listObj)
345 case *runtime.Unknown:
346 // TODO: Unstructured needs to deal with ContentType.
347 _, err := w.Write(t.Raw)
348 return err
349 default:
350 return json.NewEncoder(w).Encode(t)
351 }
352}
353
354func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) {
355 type detector struct {
356 Items gojson.RawMessage
357 }
358 var det detector
359 if err := json.Unmarshal(data, &det); err != nil {
360 return nil, err
361 }
362
363 if det.Items != nil {
364 list := &UnstructuredList{}
365 err := s.decodeToList(data, list)
366 return list, err
367 }
368
369 // No Items field, so it wasn't a list.
370 unstruct := &Unstructured{}
371 err := s.decodeToUnstructured(data, unstruct)
372 return unstruct, err
373}
374
375func (s unstructuredJSONScheme) decodeInto(data []byte, obj runtime.Object) error {
376 switch x := obj.(type) {
377 case *Unstructured:
378 return s.decodeToUnstructured(data, x)
379 case *UnstructuredList:
380 return s.decodeToList(data, x)
381 case *runtime.VersionedObjects:
382 o, err := s.decode(data)
383 if err == nil {
384 x.Objects = []runtime.Object{o}
385 }
386 return err
387 default:
388 return json.Unmarshal(data, x)
389 }
390}
391
392func (unstructuredJSONScheme) decodeToUnstructured(data []byte, unstruct *Unstructured) error {
393 m := make(map[string]interface{})
394 if err := json.Unmarshal(data, &m); err != nil {
395 return err
396 }
397
398 unstruct.Object = m
399
400 return nil
401}
402
403func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList) error {
404 type decodeList struct {
405 Items []gojson.RawMessage
406 }
407
408 var dList decodeList
409 if err := json.Unmarshal(data, &dList); err != nil {
410 return err
411 }
412
413 if err := json.Unmarshal(data, &list.Object); err != nil {
414 return err
415 }
416
417 // For typed lists, e.g., a PodList, API server doesn't set each item's
418 // APIVersion and Kind. We need to set it.
419 listAPIVersion := list.GetAPIVersion()
420 listKind := list.GetKind()
421 itemKind := strings.TrimSuffix(listKind, "List")
422
423 delete(list.Object, "items")
424 list.Items = make([]Unstructured, 0, len(dList.Items))
425 for _, i := range dList.Items {
426 unstruct := &Unstructured{}
427 if err := s.decodeToUnstructured([]byte(i), unstruct); err != nil {
428 return err
429 }
430 // This is hacky. Set the item's Kind and APIVersion to those inferred
431 // from the List.
432 if len(unstruct.GetKind()) == 0 && len(unstruct.GetAPIVersion()) == 0 {
433 unstruct.SetKind(itemKind)
434 unstruct.SetAPIVersion(listAPIVersion)
435 }
436 list.Items = append(list.Items, *unstruct)
437 }
438 return nil
439}
440
441type JSONFallbackEncoder struct {
442 runtime.Encoder
443}
444
445func (c JSONFallbackEncoder) Encode(obj runtime.Object, w io.Writer) error {
446 err := c.Encoder.Encode(obj, w)
447 if runtime.IsNotRegisteredError(err) {
448 switch obj.(type) {
449 case *Unstructured, *UnstructuredList:
450 return UnstructuredJSONScheme.Encode(obj, w)
451 }
452 }
453 return err
454}