// 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 }
// addObjectParams converts a runtime.Object into a set of go-restful Param() definitions on the route. // The object must be a pointer to a struct; only fields at the top level of the struct that are not // themselves interfaces or structs are used; only fields with a json tag that is non empty (the standard // Go JSON behavior for omitting a field) become query parameters. The name of the query parameter is // the JSON field name. If a description struct tag is set on the field, that description is used on the // query parameter. In essence, it converts a standard JSON top level object into a query param schema. func addObjectParams(ws *restful.WebService, route *restful.RouteBuilder, obj interface{}) error { sv, err := conversion.EnforcePtr(obj) if err != nil { return err } st := sv.Type() switch st.Kind() { case reflect.Struct: for i := 0; i < st.NumField(); i++ { name := st.Field(i).Name sf, ok := st.FieldByName(name) if !ok { continue } switch sf.Type.Kind() { case reflect.Interface, reflect.Struct: default: jsonTag := sf.Tag.Get("json") if len(jsonTag) == 0 { continue } jsonName := strings.SplitN(jsonTag, ",", 2)[0] if len(jsonName) == 0 { continue } desc := sf.Tag.Get("description") route.Param(ws.QueryParameter(jsonName, desc).DataType(typeToJSON(sf.Type.Name()))) } } } 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). // TODO: move me to pkg/api/meta func ExtractList(obj Object) ([]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([]Object, items.Len()) for i := range list { raw := items.Index(i) switch item := raw.Interface().(type) { case Object: list[i] = item case RawExtension: list[i] = &Unknown{ RawJSON: item.RawJSON, } default: var found bool if list[i], found = raw.Addr().Interface().(Object); !found { return nil, fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind()) } } } return list, nil }
// Implements storage.Interface. func (h *etcdHelper) Create(key string, obj, out runtime.Object, ttl uint64) error { key = h.prefixEtcdKey(key) data, err := h.codec.Encode(obj) if err != nil { return err } if h.versioner != nil { if version, err := h.versioner.ObjectResourceVersion(obj); err == nil && version != 0 { return errors.New("resourceVersion may not be set on objects to be created") } } startTime := time.Now() response, err := h.client.Create(key, string(data), ttl) metrics.RecordEtcdRequestLatency("create", getTypeName(obj), startTime) if err != nil { return err } if out != nil { if _, err := conversion.EnforcePtr(out); err != nil { panic("unable to convert output object to pointer") } _, _, err = h.extractObj(response, err, out, false, false) } return err }
// 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 }
func ListAccessor(obj interface{}) (List, error) { if listMetaAccessor, ok := obj.(ListMetaAccessor); ok { if om := listMetaAccessor.GetListMeta(); om != nil { return om, nil } } // we may get passed an object that is directly portable to List if list, ok := obj.(List); ok { return list, nil } glog.V(4).Infof("Calling ListAccessor on non-internal object: %v", reflect.TypeOf(obj)) // legacy path for objects that do not implement List and ListMetaAccessor via // reflection - very slow code path. v, err := conversion.EnforcePtr(obj) if err != nil { return nil, err } t := v.Type() if v.Kind() != reflect.Struct { return nil, fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), t, v.Interface()) } a := &genericAccessor{} listMeta := v.FieldByName("ListMeta") if listMeta.IsValid() { // look for the ListMeta fields if err := extractFromListMeta(listMeta, a); err != nil { return nil, fmt.Errorf("unable to find list fields on %#v: %v", listMeta, err) } } else { return nil, fmt.Errorf("unable to find listMeta on %#v", v) } return a, nil }
// Implements storage.Interface. func (h *etcdHelper) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error { if ctx == nil { glog.Errorf("Context is nil") } key = h.prefixEtcdKey(key) data, err := runtime.Encode(h.codec, obj) if err != nil { return err } if h.versioner != nil { if version, err := h.versioner.ObjectResourceVersion(obj); err == nil && version != 0 { return errors.New("resourceVersion may not be set on objects to be created") } } startTime := time.Now() opts := etcd.SetOptions{ TTL: time.Duration(ttl) * time.Second, PrevExist: etcd.PrevNoExist, } response, err := h.client.Set(ctx, key, string(data), &opts) metrics.RecordEtcdRequestLatency("create", getTypeName(obj), startTime) if err != nil { return err } if out != nil { if _, err := conversion.EnforcePtr(out); err != nil { panic("unable to convert output object to pointer") } _, _, err = h.extractObj(response, err, out, false, false) } return err }
func (h *etcdHelper) extractObj(response *etcd.Response, inErr error, objPtr runtime.Object, ignoreNotFound, prevNode bool) (body string, node *etcd.Node, err error) { if response != nil { if prevNode { node = response.PrevNode } else { node = response.Node } } if inErr != nil || node == nil || len(node.Value) == 0 { if ignoreNotFound { v, err := conversion.EnforcePtr(objPtr) if err != nil { return "", nil, err } v.Set(reflect.Zero(v.Type())) return "", nil, nil } else if inErr != nil { return "", nil, inErr } return "", nil, fmt.Errorf("unable to locate a value on the response: %#v", response) } body = node.Value out, gvk, err := h.codec.Decode([]byte(body), nil, objPtr) if err != nil { return body, nil, err } if out != objPtr { return body, nil, fmt.Errorf("unable to decode object %s into %v", gvk.String(), reflect.TypeOf(objPtr)) } // being unable to set the version does not prevent the object from being extracted _ = h.versioner.UpdateObject(objPtr, node.Expiration, node.ModifiedIndex) return body, node, err }
// Implements List (signature from storage.Interface). func (c *Cacher) List(key string, listObj runtime.Object) error { listPtr, err := runtime.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()) } filter := filterFunction(key, c.keyFunc, Everything) objs, resourceVersion := c.watchCache.ListWithVersion() for _, obj := range objs { object, ok := obj.(runtime.Object) if !ok { return fmt.Errorf("non runtime.Object returned from storage: %v", obj) } if filter(object) { listVal.Set(reflect.Append(listVal, reflect.ValueOf(object).Elem())) } } if c.versioner != nil { if err := c.versioner.UpdateList(listObj, resourceVersion); err != nil { return err } } return nil }
// ListFromMemory implements list operation (the same signature as List method) // but it serves the contents from memory. // Current we cannot use ListFromMemory() instead of List(), because it only // guarantees eventual consistency (e.g. it's possible for Get called right after // Create to return not-exist, before the change is propagate). // TODO: We may consider changing to use ListFromMemory in the future, but this // requires wider discussion as an "api semantic change". func (c *Cacher) ListFromMemory(key string, listObj runtime.Object) error { // Do NOT allow Watch to start when the underlying structures are not propagated. c.usable.RLock() defer c.usable.RUnlock() listPtr, err := runtime.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()) } filter := filterFunction(key, c.keyFunc, Everything) objs, resourceVersion := c.watchCache.ListWithVersion() for _, obj := range objs { object, ok := obj.(runtime.Object) if !ok { return fmt.Errorf("non runtime.Object returned from storage: %v", obj) } if filter(object) { listVal.Set(reflect.Append(listVal, reflect.ValueOf(object).Elem())) } } if c.versioner != nil { if err := c.versioner.UpdateList(listObj, resourceVersion); err != nil { return err } } 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). // TODO: move me to pkg/api/meta func ExtractList(obj Object) ([]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([]Object, items.Len()) for i := range list { raw := items.Index(i) var found bool switch raw.Kind() { case reflect.Interface, reflect.Ptr: list[i], found = raw.Interface().(Object) default: list[i], found = raw.Addr().Interface().(Object) } if !found { return nil, fmt.Errorf("item[%v]: Expected object, got %#v(%s)", i, raw.Interface(), raw.Kind()) } } return list, nil }
// Implements storage.Interface. func (c *Cacher) List(ctx context.Context, key string, resourceVersion string, pred SelectionPredicate, 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, pred, 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 } trace := util.NewTrace(fmt.Sprintf("cacher %v: List", c.objectType.String())) defer trace.LogIfLong(500 * time.Millisecond) c.ready.wait() trace.Step("Ready") // List elements with at least 'listRV' from cache. 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()) } filter := filterFunction(key, pred) objs, readResourceVersion, err := c.watchCache.WaitUntilFreshAndList(listRV, trace) if err != nil { return fmt.Errorf("failed to wait for fresh list: %v", err) } trace.Step(fmt.Sprintf("Listed %d items from cache", len(objs))) if len(objs) > listVal.Cap() && pred.Label.Empty() && pred.Field.Empty() { // Resize the slice appropriately, since we already know that none // of the elements will be filtered out. listVal.Set(reflect.MakeSlice(reflect.SliceOf(c.objectType.Elem()), 0, len(objs))) trace.Step("Resized result") } for _, obj := range objs { elem, ok := obj.(*storeElement) if !ok { return fmt.Errorf("non *storeElement returned from storage: %v", obj) } if filter(elem.Key, elem.Object) { listVal.Set(reflect.Append(listVal, reflect.ValueOf(elem.Object).Elem())) } } trace.Step(fmt.Sprintf("Filtered %d items", listVal.Len())) if c.versioner != nil { if err := c.versioner.UpdateList(listObj, readResourceVersion); err != nil { return err } } return 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 }
// 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) } }
// 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 }
// Accessor takes an arbitrary object pointer and returns meta.Interface. // obj must be a pointer to an API type. An error is returned if the minimum // required fields are missing. Fields that are not required return the default // value and are a no-op if set. func Accessor(obj interface{}) (Object, error) { if objectMetaAccessor, ok := obj.(ObjectMetaAccessor); ok { if om := objectMetaAccessor.GetObjectMeta(); om != nil { return om, nil } } // we may get passed an object that is directly portable to Object if object, ok := obj.(Object); ok { return object, nil } glog.V(4).Infof("Calling Accessor on non-internal object: %v", reflect.TypeOf(obj)) // legacy path for objects that do not implement Object and ObjectMetaAccessor via // reflection - very slow code path. v, err := conversion.EnforcePtr(obj) if err != nil { return nil, err } t := v.Type() if v.Kind() != reflect.Struct { return nil, fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), t, v.Interface()) } typeMeta := v.FieldByName("TypeMeta") if !typeMeta.IsValid() { return nil, fmt.Errorf("struct %v lacks embedded TypeMeta type", t) } a := &genericAccessor{} if err := extractFromTypeMeta(typeMeta, a); err != nil { return nil, fmt.Errorf("unable to find type fields on %#v: %v", typeMeta, err) } objectMeta := v.FieldByName("ObjectMeta") if objectMeta.IsValid() { // look for the ObjectMeta fields if err := extractFromObjectMeta(objectMeta, a); err != nil { return nil, fmt.Errorf("unable to find object fields on %#v: %v", objectMeta, err) } } else { listMeta := v.FieldByName("ListMeta") if listMeta.IsValid() { // look for the ListMeta fields if err := extractFromListMeta(listMeta, a); err != nil { return nil, fmt.Errorf("unable to find list fields on %#v: %v", listMeta, err) } } else { // look for the older TypeMeta with all metadata if err := extractFromObjectMeta(typeMeta, a); err != nil { return nil, fmt.Errorf("unable to find object fields on %#v: %v", typeMeta, err) } } } return a, nil }
// Implements storage.Interface. func (h *etcdHelper) Delete(ctx context.Context, key string, out runtime.Object, preconditions *storage.Preconditions) error { if ctx == nil { glog.Errorf("Context is nil") } key = h.prefixEtcdKey(key) v, err := conversion.EnforcePtr(out) if err != nil { panic("unable to convert output object to pointer") } if preconditions == nil { startTime := time.Now() response, err := h.etcdKeysAPI.Delete(ctx, key, nil) metrics.RecordEtcdRequestLatency("delete", getTypeName(out), startTime) if !etcdutil.IsEtcdNotFound(err) { // if the object that existed prior to the delete is returned by etcd, update the out object. if err != nil || response.PrevNode != nil { _, _, err = h.extractObj(response, err, out, false, true) } } return toStorageErr(err, key, 0) } // Check the preconditions match. obj := reflect.New(v.Type()).Interface().(runtime.Object) for { _, node, res, err := h.bodyAndExtractObj(ctx, key, obj, false) if err != nil { return toStorageErr(err, key, 0) } if err := checkPreconditions(key, preconditions, obj); err != nil { return toStorageErr(err, key, 0) } index := uint64(0) if node != nil { index = node.ModifiedIndex } else if res != nil { index = res.Index } opt := etcd.DeleteOptions{PrevIndex: index} startTime := time.Now() response, err := h.etcdKeysAPI.Delete(ctx, key, &opt) metrics.RecordEtcdRequestLatency("delete", getTypeName(out), startTime) if etcdutil.IsEtcdTestFailed(err) { glog.Infof("deletion of %s failed because of a conflict, going to retry", key) } else { if !etcdutil.IsEtcdNotFound(err) { // if the object that existed prior to the delete is returned by etcd, update the out object. if err != nil || response.PrevNode != nil { _, _, err = h.extractObj(response, err, out, false, true) } } return toStorageErr(err, key, 0) } } }
// 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) }
// Implements storage.Interface. func (c *Cacher) GetToList(ctx context.Context, key string, resourceVersion string, pred SelectionPredicate, listObj runtime.Object) error { if resourceVersion == "" { // If resourceVersion is not specified, serve it from underlying // storage (for backward compatibility). return c.storage.GetToList(ctx, key, resourceVersion, pred, 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 } trace := util.NewTrace(fmt.Sprintf("cacher %v: List", c.objectType.String())) defer trace.LogIfLong(500 * time.Millisecond) c.ready.wait() trace.Step("Ready") // List elements with at least 'listRV' from cache. 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()) } filter := filterFunction(key, pred) obj, exists, readResourceVersion, err := c.watchCache.WaitUntilFreshAndGet(listRV, key, trace) if err != nil { return fmt.Errorf("failed to wait for fresh get: %v", err) } trace.Step("Got from cache") if exists { elem, ok := obj.(*storeElement) if !ok { return fmt.Errorf("non *storeElement returned from storage: %v", obj) } if filter(elem.Key, elem.Object) { listVal.Set(reflect.Append(listVal, reflect.ValueOf(elem.Object).Elem())) } } if c.versioner != nil { if err := c.versioner.UpdateList(listObj, readResourceVersion); err != nil { return err } } return nil }
// Implements storage.Interface. func (h *etcdHelper) Set(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error { if ctx == nil { glog.Errorf("Context is nil") } var response *etcd.Response data, err := runtime.Encode(h.codec, obj) if err != nil { return err } key = h.prefixEtcdKey(key) create := true if h.versioner != nil { if version, err := h.versioner.ObjectResourceVersion(obj); err == nil && version != 0 { create = false startTime := time.Now() opts := etcd.SetOptions{ TTL: time.Duration(ttl) * time.Second, PrevIndex: version, } response, err = h.client.Set(ctx, key, string(data), &opts) metrics.RecordEtcdRequestLatency("compareAndSwap", getTypeName(obj), startTime) if err != nil { return err } } } if create { // Create will fail if a key already exists. startTime := time.Now() opts := etcd.SetOptions{ TTL: time.Duration(ttl) * time.Second, PrevExist: etcd.PrevNoExist, } response, err = h.client.Set(ctx, key, string(data), &opts) if err != nil { return err } metrics.RecordEtcdRequestLatency("create", getTypeName(obj), startTime) } if err != nil { return err } if out != nil { if _, err := conversion.EnforcePtr(out); err != nil { panic("unable to convert output object to pointer") } _, _, err = h.extractObj(response, err, out, false, false) } return err }
// 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, // or an error if it's not a pointer or is unregistered. func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, error) { v, err := conversion.EnforcePtr(obj) if err != nil { return nil, err } t := v.Type() gvks, ok := s.typeToGVK[t] if !ok { return nil, ¬RegisteredErr{t: t} } return gvks, nil }
// Implements storage.Interface. func (c *Cacher) List(ctx context.Context, key string, resourceVersion string, filter Filter, 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 } c.ready.wait() // 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.Filter(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 }
// 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) ([]schema.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, NewNotRegisteredErr(schema.GroupVersionKind{}, t) } _, unversionedType := s.unversionedTypes[t] return gvks, unversionedType, nil }
// EachListItem invokes fn on each runtime.Object in the list. Any error immediately terminates // the loop. func EachListItem(obj runtime.Object, fn func(runtime.Object) error) error { // TODO: Change to an interface call? itemsPtr, err := GetItemsPtr(obj) if err != nil { return err } items, err := conversion.EnforcePtr(itemsPtr) if err != nil { return err } len := items.Len() if len == 0 { return nil } takeAddr := false if elemType := items.Type().Elem(); elemType.Kind() != reflect.Ptr && elemType.Kind() != reflect.Interface { if !items.Index(0).CanAddr() { return fmt.Errorf("unable to take address of items in %T for EachListItem", obj) } takeAddr = true } for i := 0; i < len; i++ { raw := items.Index(i) if takeAddr { raw = raw.Addr() } switch item := raw.Interface().(type) { case *runtime.RawExtension: if err := fn(item.Object); err != nil { return err } case runtime.Object: if err := fn(item); err != nil { return err } default: obj, ok := item.(runtime.Object) if !ok { return fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind()) } if err := fn(obj); err != nil { return err } } } return nil }
// Implements storage.Interface. func (h *etcdHelper) Delete(key string, out runtime.Object) error { key = h.prefixEtcdKey(key) if _, err := conversion.EnforcePtr(out); err != nil { panic("unable to convert output object to pointer") } startTime := time.Now() response, err := h.client.Delete(key, false) metrics.RecordEtcdRequestLatency("delete", getTypeName(out), startTime) if !IsEtcdNotFound(err) { // if the object that existed prior to the delete is returned by etcd, update out. if err != nil || response.PrevNode != nil { _, _, err = h.extractObj(response, err, out, false, true) } } return err }
// 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 }
// WithType extracts a list of runtime objects with the specified type func (g *Generated) WithType(slicePtr interface{}) bool { found := false v, err := conversion.EnforcePtr(slicePtr) if err != nil || v.Kind() != reflect.Slice { // This should not happen at runtime. panic("need ptr to slice") } t := v.Type().Elem() for i := range g.Items { obj := reflect.ValueOf(g.Items[i]).Elem() if !obj.Type().ConvertibleTo(t) { continue } found = true v.Set(reflect.Append(v, obj.Convert(t))) } return found }