// 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 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 }
// List implements storage.Interface.List. func (s *store) List(ctx context.Context, key, resourceVersion string, pred storage.SelectionPredicate, listObj runtime.Object) error { listPtr, err := meta.GetItemsPtr(listObj) if err != nil { return err } key = path.Join(s.pathPrefix, key) // We need to make sure the key ended with "/" so that we only get children "directories". // e.g. if we have key "/a", "/a/b", "/ab", getting keys with prefix "/a" will return all three, // while with prefix "/a/" will return only "/a/b" which is the correct answer. if !strings.HasSuffix(key, "/") { key += "/" } getResp, err := s.client.KV.Get(ctx, key, clientv3.WithPrefix()) if err != nil { return err } elems := make([]*elemForDecode, len(getResp.Kvs)) for i, kv := range getResp.Kvs { elems[i] = &elemForDecode{ data: kv.Value, rev: uint64(kv.ModRevision), } } if err := decodeList(elems, storage.SimpleFilter(pred), listPtr, s.codec, s.versioner); err != nil { return err } // update version with cluster level revision return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision)) }
// Implements storage.Interface. func (h *etcdHelper) List(ctx context.Context, key string, resourceVersion string, pred storage.SelectionPredicate, listObj runtime.Object) error { if ctx == nil { glog.Errorf("Context is nil") } trace := util.NewTrace("List " + getTypeName(listObj)) defer trace.LogIfLong(400 * time.Millisecond) listPtr, err := meta.GetItemsPtr(listObj) if err != nil { return err } key = path.Join(h.pathPrefix, key) startTime := time.Now() trace.Step("About to list etcd node") nodes, index, err := h.listEtcdNode(ctx, key) trace.Step("Etcd node listed") metrics.RecordEtcdRequestLatency("list", getTypeName(listPtr), startTime) if err != nil { return err } if err := h.decodeNodeList(nodes, storage.SimpleFilter(pred), listPtr); err != nil { return err } trace.Step("Node list decoded") if err := h.versioner.UpdateList(listObj, index); err != nil { return err } return nil }
// 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 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) GetToList(ctx context.Context, key string, resourceVersion string, pred storage.SelectionPredicate, listObj runtime.Object) error { if ctx == nil { glog.Errorf("Context is nil") } trace := util.NewTrace("GetToList " + getTypeName(listObj)) listPtr, err := meta.GetItemsPtr(listObj) if err != nil { return err } key = path.Join(h.pathPrefix, key) startTime := time.Now() trace.Step("About to read etcd node") opts := &etcd.GetOptions{ Quorum: h.quorum, } response, err := h.etcdKeysAPI.Get(ctx, key, opts) trace.Step("Etcd node read") metrics.RecordEtcdRequestLatency("get", getTypeName(listPtr), startTime) if err != nil { if etcdutil.IsEtcdNotFound(err) { return nil } return toStorageErr(err, key, 0) } nodes := make([]*etcd.Node, 0) nodes = append(nodes, response.Node) if err := h.decodeNodeList(nodes, storage.SimpleFilter(pred), listPtr); err != nil { return err } trace.Step("Object decoded") if err := h.versioner.UpdateList(listObj, response.Index); err != nil { return err } return nil }
// GetToList implements storage.Interface.GetToList. func (s *store) GetToList(ctx context.Context, key string, resourceVersion string, pred storage.SelectionPredicate, listObj runtime.Object) error { listPtr, err := meta.GetItemsPtr(listObj) if err != nil { return err } key = path.Join(s.pathPrefix, key) getResp, err := s.client.KV.Get(ctx, key, s.getOps...) if err != nil { return err } if len(getResp.Kvs) == 0 { return nil } elems := []*elemForDecode{{ data: getResp.Kvs[0].Value, rev: uint64(getResp.Kvs[0].ModRevision), }} if err := decodeList(elems, storage.SimpleFilter(pred), listPtr, s.codec, s.versioner); err != nil { return err } // update version with cluster level revision return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision)) }