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