2 Copyright 2015 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
20 gojson "encoding/json"
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"
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 func 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
40 return runtime.DeepCopyJSONValue(val), true, nil
43 // NestedFieldNoCopy returns a reference to a nested field.
44 // Returns false if value is not found and an error if unable
46 func NestedFieldNoCopy(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
47 var val interface{} = obj
49 for i, field := range fields {
50 if m, ok := val.(map[string]interface{}); ok {
53 return nil, false, nil
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)
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.
64 func NestedString(obj map[string]interface{}, fields ...string) (string, bool, error) {
65 val, found, err := NestedFieldNoCopy(obj, fields...)
66 if !found || err != nil {
71 return "", false, fmt.Errorf("%v accessor error: %v is of the type %T, expected string", jsonPath(fields), val, val)
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.
78 func 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
85 return false, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected bool", jsonPath(fields), val, val)
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.
92 func NestedFloat64(obj map[string]interface{}, fields ...string) (float64, bool, error) {
93 val, found, err := NestedFieldNoCopy(obj, fields...)
94 if !found || err != nil {
97 f, ok := val.(float64)
99 return 0, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected float64", jsonPath(fields), val, val)
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.
106 func NestedInt64(obj map[string]interface{}, fields ...string) (int64, bool, error) {
107 val, found, err := NestedFieldNoCopy(obj, fields...)
108 if !found || err != nil {
113 return 0, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected int64", jsonPath(fields), val, val)
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.
120 func 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
125 m, ok := val.([]interface{})
127 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected []interface{}", jsonPath(fields), val, val)
129 strSlice := make([]string, 0, len(m))
130 for _, v := range m {
131 if str, ok := v.(string); ok {
132 strSlice = append(strSlice, str)
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)
137 return strSlice, true, nil
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{}.
142 func 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
147 _, ok := val.([]interface{})
149 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected []interface{}", jsonPath(fields), val, val)
151 return runtime.DeepCopyJSONValue(val).([]interface{}), true, nil
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.
156 func 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
161 strMap := make(map[string]string, len(m))
162 for k, v := range m {
163 if str, ok := v.(string); ok {
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)
169 return strMap, true, nil
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{}.
174 func 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
179 return runtime.DeepCopyJSON(m), true, nil
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{}.
184 func 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
189 m, ok := val.(map[string]interface{})
191 return nil, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected map[string]interface{}", jsonPath(fields), val, val)
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{}.
198 func SetNestedField(obj map[string]interface{}, value interface{}, fields ...string) error {
199 return setNestedFieldNoCopy(obj, runtime.DeepCopyJSONValue(value), fields...)
202 func setNestedFieldNoCopy(obj map[string]interface{}, value interface{}, fields ...string) error {
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 {
210 return fmt.Errorf("value cannot be set because %v is not a map[string]interface{}", jsonPath(fields[:i+1]))
213 newVal := make(map[string]interface{})
218 m[fields[len(fields)-1]] = value
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{}.
224 func 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 {
229 return setNestedFieldNoCopy(obj, m, fields...)
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{}.
234 func SetNestedSlice(obj map[string]interface{}, value []interface{}, fields ...string) error {
235 return SetNestedField(obj, value, fields...)
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{}.
240 func 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 {
245 return setNestedFieldNoCopy(obj, m, fields...)
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{}.
250 func SetNestedMap(obj map[string]interface{}, value map[string]interface{}, fields ...string) error {
251 return SetNestedField(obj, value, fields...)
254 // RemoveNestedField removes the nested field from the obj.
255 func RemoveNestedField(obj map[string]interface{}, fields ...string) {
257 for _, field := range fields[:len(fields)-1] {
258 if x, ok := m[field].(map[string]interface{}); ok {
264 delete(m, fields[len(fields)-1])
267 func getNestedString(obj map[string]interface{}, fields ...string) string {
268 val, found, err := NestedString(obj, fields...)
269 if !found || err != nil {
275 func jsonPath(fields []string) string {
276 return "." + strings.Join(fields, ".")
279 func 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
286 var blockOwnerDeletionPtr *bool
287 if blockOwnerDeletion, found, err := NestedBool(v, "blockOwnerDeletion"); err == nil && found {
288 blockOwnerDeletionPtr = &blockOwnerDeletion
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,
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.
303 var UnstructuredJSONScheme runtime.Codec = unstructuredJSONScheme{}
305 type unstructuredJSONScheme struct{}
307 func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
310 err = s.decodeInto(data, obj)
312 obj, err = s.decode(data)
319 gvk := obj.GetObjectKind().GroupVersionKind()
320 if len(gvk.Kind) == 0 {
321 return nil, &gvk, runtime.NewMissingKindErr(string(data))
324 return obj, &gvk, nil
327 func (unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
328 switch t := obj.(type) {
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)
336 listObj := make(map[string]interface{}, len(t.Object)+1)
337 for k, v := range t.Object { // Make a shallow copy
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)
347 return json.NewEncoder(w).Encode(t)
351 func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) {
352 type detector struct {
353 Items gojson.RawMessage
356 if err := json.Unmarshal(data, &det); err != nil {
360 if det.Items != nil {
361 list := &UnstructuredList{}
362 err := s.decodeToList(data, list)
366 // No Items field, so it wasn't a list.
367 unstruct := &Unstructured{}
368 err := s.decodeToUnstructured(data, unstruct)
372 func (s unstructuredJSONScheme) decodeInto(data []byte, obj runtime.Object) error {
373 switch x := obj.(type) {
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)
381 x.Objects = []runtime.Object{o}
385 return json.Unmarshal(data, x)
389 func (unstructuredJSONScheme) decodeToUnstructured(data []byte, unstruct *Unstructured) error {
390 m := make(map[string]interface{})
391 if err := json.Unmarshal(data, &m); err != nil {
400 func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList) error {
401 type decodeList struct {
402 Items []gojson.RawMessage
406 if err := json.Unmarshal(data, &dList); err != nil {
410 if err := json.Unmarshal(data, &list.Object); err != nil {
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")
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 {
427 // This is hacky. Set the item's Kind and APIVersion to those inferred
429 if len(unstruct.GetKind()) == 0 && len(unstruct.GetAPIVersion()) == 0 {
430 unstruct.SetKind(itemKind)
431 unstruct.SetAPIVersion(listAPIVersion)
433 list.Items = append(list.Items, *unstruct)
438 type JSONFallbackEncoder struct {
442 func (c JSONFallbackEncoder) Encode(obj runtime.Object, w io.Writer) error {
443 err := c.Encoder.Encode(obj, w)
444 if runtime.IsNotRegisteredError(err) {
446 case *Unstructured, *UnstructuredList:
447 return UnstructuredJSONScheme.Encode(obj, w)