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