// 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 = keyWithPrefix(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, filter storage.FilterFunc, listObj runtime.Object) error { if ctx == nil { glog.Errorf("Context is nil") } trace := util.NewTrace("List " + getTypeName(listObj)) defer trace.LogIfLong(time.Second) listPtr, err := meta.GetItemsPtr(listObj) if err != nil { return err } key = h.prefixEtcdKey(key) startTime := time.Now() trace.Step("About to list etcd node") nodes, index, err := h.listEtcdNode(ctx, key) metrics.RecordEtcdRequestLatency("list", getTypeName(listPtr), startTime) trace.Step("Etcd node listed") if err != nil { return err } if err := h.decodeNodeList(nodes, filter, 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) 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 }
// 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 }
// 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 (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 }
// Implements storage.Interface. func (h *etcdHelper) GetToList(ctx context.Context, key string, filter storage.FilterFunc, 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 = h.prefixEtcdKey(key) startTime := time.Now() trace.Step("About to read etcd node") opts := &etcd.GetOptions{ Quorum: h.quorum, } response, err := h.client.Get(ctx, key, opts) metrics.RecordEtcdRequestLatency("get", getTypeName(listPtr), startTime) trace.Step("Etcd node read") if err != nil { if etcdutil.IsEtcdNotFound(err) { return nil } return err } nodes := make([]*etcd.Node, 0) nodes = append(nodes, response.Node) if err := h.decodeNodeList(nodes, filter, listPtr); err != nil { return err } trace.Step("Object decoded") if h.versioner != nil { if err := h.versioner.UpdateList(listObj, response.Index); err != nil { return err } } return nil }
// Implements storage.Interface. func (c *Cacher) List(ctx context.Context, key string, resourceVersion uint64, filter FilterFunc, listObj runtime.Object) error { if !c.ListFromCache { return c.storage.List(ctx, key, resourceVersion, filter, listObj) } // To avoid situation when List is proceesed 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 'resourceVersion'. 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, resourceVersion := c.watchCache.WaitUntilFreshAndList(resourceVersion) 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, resourceVersion); 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 = keyWithPrefix(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)) }