Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Support multiple field selectors #1838

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 54 additions & 14 deletions pkg/cache/internal/cache_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,59 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli
listOpts.ApplyOptions(opts)

switch {
case listOpts.FieldSelector != nil:
case listOpts.FieldSelector != nil && !listOpts.FieldSelector.Empty():
// TODO(directxman12): support more complicated field selectors by
// combining multiple indices, GetIndexers, etc
field, val, requiresExact := requiresExactMatch(listOpts.FieldSelector)
requiresExact := requiresExactMatch(listOpts.FieldSelector)
if !requiresExact {
return fmt.Errorf("non-exact field matches are not supported by the cache")
}
// list all objects by the field selector. If this is namespaced and we have one, ask for the
// namespaced index key. Otherwise, ask for the non-namespaced variant by using the fake "all namespaces"
// namespace.
objs, err = c.indexer.ByIndex(FieldIndexName(field), KeyToNamespacedKey(listOpts.Namespace, val))

reqs := listOpts.FieldSelector.Requirements()
// len(reqs) == 0 means, select nothing
if len(reqs) > 0 {
req := reqs[0]
// list all objects by the field selector. If this is namespaced and we have one, ask for the
// namespaced index key. Otherwise, ask for the non-namespaced variant by using the fake "all namespaces"
// namespace.
list, err := c.indexer.ByIndex(FieldIndexName(req.Field), KeyToNamespacedKey(listOpts.Namespace, req.Value))
if err != nil {
return err
}
if len(reqs) > 1 {
objmap := make(map[client.ObjectKey]interface{}, len(list))
for i := range list {
obj := list[i].(client.Object)
objmap[client.ObjectKey{Namespace: obj.GetNamespace(), Name: obj.GetName()}] = obj
}
for _, req := range reqs[1:] {
list, err := c.indexer.ByIndex(FieldIndexName(req.Field), KeyToNamespacedKey(listOpts.Namespace, req.Value))
if err != nil {
return err
}

numap := make(map[client.ObjectKey]interface{}, len(list))
for i := range list {
obj := list[i].(client.Object)
key := client.ObjectKey{Namespace: obj.GetNamespace(), Name: obj.GetName()}
if o, exists := objmap[key]; exists {
if o.(client.Object).GetGeneration() == obj.GetGeneration() {
numap[key] = obj
} else {
return fmt.Errorf("multiple generation found in indices for %+v %s/%s", obj.GetObjectKind().GroupVersionKind(), obj.GetNamespace(), obj.GetName())
}
}
}
objmap = numap
}
objs = make([]interface{}, 0, len(objmap))
for _, obj := range objmap {
objs = append(objs, obj)
}
} else {
objs = list
}
}
case listOpts.Namespace != "":
objs, err = c.indexer.ByIndex(cache.NamespaceIndex, listOpts.Namespace)
default:
Expand Down Expand Up @@ -187,16 +229,14 @@ func objectKeyToStoreKey(k client.ObjectKey) string {
}

// requiresExactMatch checks if the given field selector is of the form `k=v` or `k==v`.
func requiresExactMatch(sel fields.Selector) (field, val string, required bool) {
func requiresExactMatch(sel fields.Selector) (required bool) {
reqs := sel.Requirements()
if len(reqs) != 1 {
return "", "", false
}
req := reqs[0]
if req.Operator != selection.Equals && req.Operator != selection.DoubleEquals {
return "", "", false
for _, req := range reqs {
if req.Operator != selection.Equals && req.Operator != selection.DoubleEquals {
return false
}
}
return req.Field, req.Value, true
return true
}

// FieldIndexName constructs the name of the index over the given field,
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ type ListOptions struct {
LabelSelector labels.Selector
// FieldSelector filters results by a particular field. In order
// to use this with cache-based implementations, restrict usage to
// a single field-value pair that's been added to the indexers.
// field-value pair exact matches that's been added to the indexers.
FieldSelector fields.Selector

// Namespace represents the namespace to list for, or empty for
Expand Down