github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/api/meta/help.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 meta
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"reflect"
    23  	"sync"
    24  
    25  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/conversion"
    26  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    27  )
    28  
    29  var (
    30  	// isListCache maintains a cache of types that are checked for lists
    31  	// which is used by IsListType.
    32  	// TODO: remove and replace with an interface check
    33  	isListCache = struct {
    34  		lock   sync.RWMutex
    35  		byType map[reflect.Type]bool
    36  	}{
    37  		byType: make(map[reflect.Type]bool, 1024),
    38  	}
    39  )
    40  
    41  // IsListType returns true if the provided Object has a slice called Items.
    42  // TODO: Replace the code in this check with an interface comparison by
    43  //
    44  //	creating and enforcing that lists implement a list accessor.
    45  func IsListType(obj runtime.Object) bool {
    46  	switch t := obj.(type) {
    47  	case runtime.Unstructured:
    48  		return t.IsList()
    49  	}
    50  	t := reflect.TypeOf(obj)
    51  
    52  	isListCache.lock.RLock()
    53  	ok, exists := isListCache.byType[t]
    54  	isListCache.lock.RUnlock()
    55  
    56  	if !exists {
    57  		_, err := getItemsPtr(obj)
    58  		ok = err == nil
    59  
    60  		// cache only the first 1024 types
    61  		isListCache.lock.Lock()
    62  		if len(isListCache.byType) < 1024 {
    63  			isListCache.byType[t] = ok
    64  		}
    65  		isListCache.lock.Unlock()
    66  	}
    67  
    68  	return ok
    69  }
    70  
    71  var (
    72  	errExpectFieldItems = errors.New("no Items field in this object")
    73  	errExpectSliceItems = errors.New("Items field must be a slice of objects")
    74  )
    75  
    76  // GetItemsPtr returns a pointer to the list object's Items member.
    77  // If 'list' doesn't have an Items member, it's not really a list type
    78  // and an error will be returned.
    79  // This function will either return a pointer to a slice, or an error, but not both.
    80  // TODO: this will be replaced with an interface in the future
    81  func GetItemsPtr(list runtime.Object) (interface{}, error) {
    82  	obj, err := getItemsPtr(list)
    83  	if err != nil {
    84  		return nil, fmt.Errorf("%T is not a list: %v", list, err)
    85  	}
    86  	return obj, nil
    87  }
    88  
    89  // getItemsPtr returns a pointer to the list object's Items member or an error.
    90  func getItemsPtr(list runtime.Object) (interface{}, error) {
    91  	v, err := conversion.EnforcePtr(list)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	items := v.FieldByName("Items")
    97  	if !items.IsValid() {
    98  		return nil, errExpectFieldItems
    99  	}
   100  	switch items.Kind() {
   101  	case reflect.Interface, reflect.Pointer:
   102  		target := reflect.TypeOf(items.Interface()).Elem()
   103  		if target.Kind() != reflect.Slice {
   104  			return nil, errExpectSliceItems
   105  		}
   106  		return items.Interface(), nil
   107  	case reflect.Slice:
   108  		return items.Addr().Interface(), nil
   109  	default:
   110  		return nil, errExpectSliceItems
   111  	}
   112  }
   113  
   114  // EachListItem invokes fn on each runtime.Object in the list. Any error immediately terminates
   115  // the loop.
   116  func EachListItem(obj runtime.Object, fn func(runtime.Object) error) error {
   117  	if unstructured, ok := obj.(runtime.Unstructured); ok {
   118  		return unstructured.EachListItem(fn)
   119  	}
   120  	// TODO: Change to an interface call?
   121  	itemsPtr, err := GetItemsPtr(obj)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	items, err := conversion.EnforcePtr(itemsPtr)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	len := items.Len()
   130  	if len == 0 {
   131  		return nil
   132  	}
   133  	takeAddr := false
   134  	if elemType := items.Type().Elem(); elemType.Kind() != reflect.Pointer && elemType.Kind() != reflect.Interface {
   135  		if !items.Index(0).CanAddr() {
   136  			return fmt.Errorf("unable to take address of items in %T for EachListItem", obj)
   137  		}
   138  		takeAddr = true
   139  	}
   140  
   141  	for i := 0; i < len; i++ {
   142  		raw := items.Index(i)
   143  		if takeAddr {
   144  			raw = raw.Addr()
   145  		}
   146  		switch item := raw.Interface().(type) {
   147  		case *runtime.RawExtension:
   148  			if err := fn(item.Object); err != nil {
   149  				return err
   150  			}
   151  		case runtime.Object:
   152  			if err := fn(item); err != nil {
   153  				return err
   154  			}
   155  		default:
   156  			obj, ok := item.(runtime.Object)
   157  			if !ok {
   158  				return fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind())
   159  			}
   160  			if err := fn(obj); err != nil {
   161  				return err
   162  			}
   163  		}
   164  	}
   165  	return nil
   166  }
   167  
   168  // ExtractList returns obj's Items element as an array of runtime.Objects.
   169  // Returns an error if obj is not a List type (does not have an Items member).
   170  func ExtractList(obj runtime.Object) ([]runtime.Object, error) {
   171  	itemsPtr, err := GetItemsPtr(obj)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	items, err := conversion.EnforcePtr(itemsPtr)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  	list := make([]runtime.Object, items.Len())
   180  	for i := range list {
   181  		raw := items.Index(i)
   182  		switch item := raw.Interface().(type) {
   183  		case runtime.RawExtension:
   184  			switch {
   185  			case item.Object != nil:
   186  				list[i] = item.Object
   187  			case item.Raw != nil:
   188  				// TODO: Set ContentEncoding and ContentType correctly.
   189  				list[i] = &runtime.Unknown{Raw: item.Raw}
   190  			default:
   191  				list[i] = nil
   192  			}
   193  		case runtime.Object:
   194  			list[i] = item
   195  		default:
   196  			var found bool
   197  			if list[i], found = raw.Addr().Interface().(runtime.Object); !found {
   198  				return nil, fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind())
   199  			}
   200  		}
   201  	}
   202  	return list, nil
   203  }
   204  
   205  // objectSliceType is the type of a slice of Objects
   206  var objectSliceType = reflect.TypeOf([]runtime.Object{})
   207  
   208  // LenList returns the length of this list or 0 if it is not a list.
   209  func LenList(list runtime.Object) int {
   210  	itemsPtr, err := GetItemsPtr(list)
   211  	if err != nil {
   212  		return 0
   213  	}
   214  	items, err := conversion.EnforcePtr(itemsPtr)
   215  	if err != nil {
   216  		return 0
   217  	}
   218  	return items.Len()
   219  }
   220  
   221  // SetList sets the given list object's Items member have the elements given in
   222  // objects.
   223  // Returns an error if list is not a List type (does not have an Items member),
   224  // or if any of the objects are not of the right type.
   225  func SetList(list runtime.Object, objects []runtime.Object) error {
   226  	itemsPtr, err := GetItemsPtr(list)
   227  	if err != nil {
   228  		return err
   229  	}
   230  	items, err := conversion.EnforcePtr(itemsPtr)
   231  	if err != nil {
   232  		return err
   233  	}
   234  	if items.Type() == objectSliceType {
   235  		items.Set(reflect.ValueOf(objects))
   236  		return nil
   237  	}
   238  	slice := reflect.MakeSlice(items.Type(), len(objects), len(objects))
   239  	for i := range objects {
   240  		dest := slice.Index(i)
   241  		if dest.Type() == reflect.TypeOf(runtime.RawExtension{}) {
   242  			dest = dest.FieldByName("Object")
   243  		}
   244  
   245  		// check to see if you're directly assignable
   246  		if reflect.TypeOf(objects[i]).AssignableTo(dest.Type()) {
   247  			dest.Set(reflect.ValueOf(objects[i]))
   248  			continue
   249  		}
   250  
   251  		src, err := conversion.EnforcePtr(objects[i])
   252  		if err != nil {
   253  			return err
   254  		}
   255  		if src.Type().AssignableTo(dest.Type()) {
   256  			dest.Set(src)
   257  		} else if src.Type().ConvertibleTo(dest.Type()) {
   258  			dest.Set(src.Convert(dest.Type()))
   259  		} else {
   260  			return fmt.Errorf("item[%d]: can't assign or convert %v into %v", i, src.Type(), dest.Type())
   261  		}
   262  	}
   263  	items.Set(slice)
   264  	return nil
   265  }