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 }