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  }