// SetList sets the given list object's Items member have the elements given in // objects. // Returns an error if list is not a List type (does not have an Items member), // or if any of the objects are not of the right type. func SetList(list runtime.Object, objects []runtime.Object) error { itemsPtr, err := GetItemsPtr(list) if err != nil { return err } items, err := conversion.EnforcePtr(itemsPtr) if err != nil { return err } if items.Type() == objectSliceType { items.Set(reflect.ValueOf(objects)) return nil } slice := reflect.MakeSlice(items.Type(), len(objects), len(objects)) for i := range objects { dest := slice.Index(i) src, err := conversion.EnforcePtr(objects[i]) if err != nil { return err } if src.Type().AssignableTo(dest.Type()) { dest.Set(src) } else if src.Type().ConvertibleTo(dest.Type()) { dest.Set(src.Convert(dest.Type())) } else { return fmt.Errorf("item[%d]: can't assign or convert %v into %v", i, src.Type(), dest.Type()) } } items.Set(slice) return nil }
// ExtractList returns obj's Items element as an array of runtime.Objects. // Returns an error if obj is not a List type (does not have an Items member). func ExtractList(obj runtime.Object) ([]runtime.Object, error) { itemsPtr, err := GetItemsPtr(obj) if err != nil { return nil, err } items, err := conversion.EnforcePtr(itemsPtr) if err != nil { return nil, err } list := make([]runtime.Object, items.Len()) for i := range list { raw := items.Index(i) switch item := raw.Interface().(type) { case runtime.RawExtension: switch { case item.Object != nil: list[i] = item.Object case item.Raw != nil: // TODO: Set ContentEncoding and ContentType correctly. list[i] = &runtime.Unknown{Raw: item.Raw} default: list[i] = nil } case runtime.Object: list[i] = item default: var found bool if list[i], found = raw.Addr().Interface().(runtime.Object); !found { return nil, fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind()) } } } return list, nil }
// SetZeroValue would set the object of objPtr to zero value of its type. func SetZeroValue(objPtr Object) error { v, err := conversion.EnforcePtr(objPtr) if err != nil { return err } v.Set(reflect.Zero(v.Type())) return nil }
// ListMetaFor returns a pointer to a provided object's ListMeta, // or an error if the object does not have that pointer. // TODO: allow runtime.Unknown to extract this object func ListMetaFor(obj runtime.Object) (*unversioned.ListMeta, error) { v, err := conversion.EnforcePtr(obj) if err != nil { return nil, err } var meta *unversioned.ListMeta err = runtime.FieldPtr(v, "ListMeta", &meta) return meta, err }
// GuaranteedUpdate implements storage.Interface.GuaranteedUpdate. func (s *store) GuaranteedUpdate(ctx context.Context, key string, out runtime.Object, ignoreNotFound bool, precondtions *storage.Preconditions, tryUpdate storage.UpdateFunc) error { v, err := conversion.EnforcePtr(out) if err != nil { panic("unable to convert output object to pointer") } key = keyWithPrefix(s.pathPrefix, key) getResp, err := s.client.KV.Get(ctx, key) if err != nil { return err } for { origState, err := s.getState(getResp, key, v, ignoreNotFound) if err != nil { return err } if err := checkPreconditions(key, precondtions, origState.obj); err != nil { return err } ret, ttl, err := s.updateState(origState, tryUpdate) if err != nil { return err } data, err := runtime.Encode(s.codec, ret) if err != nil { return err } if bytes.Equal(data, origState.data) { return decode(s.codec, s.versioner, origState.data, out, origState.rev) } opts, err := s.ttlOpts(ctx, int64(ttl)) if err != nil { return err } txnResp, err := s.client.KV.Txn(ctx).If( clientv3.Compare(clientv3.ModRevision(key), "=", origState.rev), ).Then( clientv3.OpPut(key, string(data), opts...), ).Else( clientv3.OpGet(key), ).Commit() if err != nil { return err } if !txnResp.Succeeded { getResp = (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange()) glog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", key) continue } putResp := txnResp.Responses[0].GetResponsePut() return decode(s.codec, s.versioner, data, out, putResp.Header.Revision) } }
// Implements storage.Interface. func (c *Cacher) List(ctx context.Context, key string, resourceVersion string, filter FilterFunc, listObj runtime.Object) error { if resourceVersion == "" { // If resourceVersion is not specified, serve it from underlying // storage (for backward compatibility). return c.storage.List(ctx, key, resourceVersion, filter, listObj) } // If resourceVersion is specified, serve it from cache. // It's guaranteed that the returned value is at least that // fresh as the given resourceVersion. listRV, err := ParseListResourceVersion(resourceVersion) if err != nil { return err } // To avoid situation when List is processed before the underlying // watchCache is propagated for the first time, we acquire and immediately // release the 'usable' lock. // We don't need to hold it all the time, because watchCache is thread-safe // and it would complicate already very difficult locking pattern. c.usable.RLock() c.usable.RUnlock() // List elements from cache, with at least 'listRV'. listPtr, err := meta.GetItemsPtr(listObj) if err != nil { return err } listVal, err := conversion.EnforcePtr(listPtr) if err != nil || listVal.Kind() != reflect.Slice { return fmt.Errorf("need a pointer to slice, got %v", listVal.Kind()) } filterFunc := filterFunction(key, c.keyFunc, filter) objs, readResourceVersion, err := c.watchCache.WaitUntilFreshAndList(listRV) if err != nil { return fmt.Errorf("failed to wait for fresh list: %v", err) } for _, obj := range objs { object, ok := obj.(runtime.Object) if !ok { return fmt.Errorf("non runtime.Object returned from storage: %v", obj) } if filterFunc(object) { listVal.Set(reflect.Append(listVal, reflect.ValueOf(object).Elem())) } } if c.versioner != nil { if err := c.versioner.UpdateList(listObj, readResourceVersion); err != nil { return err } } return nil }
// Delete implements storage.Interface.Delete. func (s *store) Delete(ctx context.Context, key string, out runtime.Object, precondtions *storage.Preconditions) error { v, err := conversion.EnforcePtr(out) if err != nil { panic("unable to convert output object to pointer") } key = keyWithPrefix(s.pathPrefix, key) if precondtions == nil { return s.unconditionalDelete(ctx, key, out) } return s.conditionalDelete(ctx, key, out, v, precondtions) }
// decode decodes value of bytes into object. It will also set the object resource version to rev. // On success, objPtr would be set to the object. func decode(codec runtime.Codec, versioner storage.Versioner, value []byte, objPtr runtime.Object, rev int64) error { if _, err := conversion.EnforcePtr(objPtr); err != nil { panic("unable to convert output object to pointer") } _, _, err := codec.Decode(value, nil, objPtr) if err != nil { return err } // being unable to set the version does not prevent the object from being extracted versioner.UpdateObject(objPtr, uint64(rev)) return nil }
func (s *Scheme) IsUnversioned(obj Object) (bool, bool) { v, err := conversion.EnforcePtr(obj) if err != nil { return false, false } t := v.Type() if _, ok := s.typeToGVK[t]; !ok { return false, false } _, ok := s.unversionedTypes[t] return ok, true }
// ObjectKinds returns all possible group,version,kind of the go object, true if the // object is considered unversioned, or an error if it's not a pointer or is unregistered. func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, bool, error) { v, err := conversion.EnforcePtr(obj) if err != nil { return nil, false, err } t := v.Type() gvks, ok := s.typeToGVK[t] if !ok { return nil, false, ¬RegisteredErr{t: t} } _, unversionedType := s.unversionedTypes[t] return gvks, unversionedType, nil }
func listToItems(listObj runtime.Object) ([]runtime.Object, error) { v, err := conversion.EnforcePtr(listObj) if err != nil { return nil, fmt.Errorf("unexpected error: %v", err) } items := v.FieldByName("Items") if !items.IsValid() { return nil, fmt.Errorf("unexpected Items field in %v", listObj) } if items.Type().Kind() != reflect.Slice { return nil, fmt.Errorf("unexpected Items field type: %v", items.Type().Kind()) } result := make([]runtime.Object, items.Len()) for i := 0; i < items.Len(); i++ { result[i] = items.Index(i).Addr().Interface().(runtime.Object) } return result, nil }
// decodeList decodes a list of values into a list of objects, with resource version set to corresponding rev. // On success, ListPtr would be set to the list of objects. func decodeList(elems []*elemForDecode, filter storage.FilterFunc, ListPtr interface{}, codec runtime.Codec, versioner storage.Versioner) error { v, err := conversion.EnforcePtr(ListPtr) if err != nil || v.Kind() != reflect.Slice { panic("need ptr to slice") } for _, elem := range elems { obj, _, err := codec.Decode(elem.data, nil, reflect.New(v.Type().Elem()).Interface().(runtime.Object)) if err != nil { return err } // being unable to set the version does not prevent the object from being extracted versioner.UpdateObject(obj, elem.rev) if filter(obj) { v.Set(reflect.Append(v, reflect.ValueOf(obj).Elem())) } } return nil }
// Field puts the value of fieldName, which must be a member of v, into dest, // which must be a variable to which this field's value can be assigned. func Field(v reflect.Value, fieldName string, dest interface{}) error { field := v.FieldByName(fieldName) if !field.IsValid() { return fmt.Errorf("couldn't find %v field in %#v", fieldName, v.Interface()) } destValue, err := conversion.EnforcePtr(dest) if err != nil { return err } if field.Type().AssignableTo(destValue.Type()) { destValue.Set(field) return nil } if field.Type().ConvertibleTo(destValue.Type()) { destValue.Set(field.Convert(destValue.Type())) return nil } return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), destValue.Type()) }
// GetItemsPtr returns a pointer to the list object's Items member. // If 'list' doesn't have an Items member, it's not really a list type // and an error will be returned. // This function will either return a pointer to a slice, or an error, but not both. func GetItemsPtr(list runtime.Object) (interface{}, error) { v, err := conversion.EnforcePtr(list) if err != nil { return nil, err } items := v.FieldByName("Items") if !items.IsValid() { return nil, fmt.Errorf("no Items field in %#v", list) } switch items.Kind() { case reflect.Interface, reflect.Ptr: target := reflect.TypeOf(items.Interface()).Elem() if target.Kind() != reflect.Slice { return nil, fmt.Errorf("items: Expected slice, got %s", target.Kind()) } return items.Interface(), nil case reflect.Slice: return items.Addr().Interface(), nil default: return nil, fmt.Errorf("items: Expected slice, got %s", items.Kind()) } }