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