// List implements storage.Interface.List. func (s *store) List(ctx context.Context, key, resourceVersion string, filter storage.FilterFunc, 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, filter, 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 (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 }
// GetToList implements storage.Interface.GetToList. func (s *store) GetToList(ctx context.Context, key string, filter storage.FilterFunc, 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) 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, filter, listPtr, s.codec, s.versioner); err != nil { return err } // update version with cluster level revision return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision)) }