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