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.
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}
455