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