Merge pull request #126875 from serathius/watchcache-test-indexers

Adding tests for using indexers in tests

Kubernetes-commit: a0e5e244b3fcfc60d2bf2296c63a72e015f5884b
This commit is contained in:
Kubernetes Publisher 2024-10-30 01:21:32 +00:00
commit 28f9eed685
6 changed files with 600 additions and 41 deletions

View File

@ -256,6 +256,12 @@ func TestListResourceVersionMatch(t *testing.T) {
// TODO(#109831): Enable use of this test and run it.
}
func TestNamespaceScopedList(t *testing.T) {
ctx, cacher, terminate := testSetup(t, withSpecNodeNameIndexerFuncs)
t.Cleanup(terminate)
storagetesting.RunTestNamespaceScopedList(ctx, t, cacher)
}
func TestGuaranteedUpdate(t *testing.T) {
// TODO(#109831): Enable use of this test and run it.
}

View File

@ -2943,3 +2943,149 @@ func forceRequestWatchProgressSupport(t *testing.T) {
t.Fatalf("failed to wait for required %v storage feature to initialize", storage.RequestWatchProgress)
}
}
func TestListIndexer(t *testing.T) {
ctx, cacher, terminate := testSetup(t, withSpecNodeNameIndexerFuncs)
t.Cleanup(terminate)
tests := []struct {
name string
requestedNamespace string
recursive bool
fieldSelector fields.Selector
indexFields []string
expectIndex string
}{
{
name: "request without namespace, without field selector",
recursive: true,
fieldSelector: fields.Everything(),
},
{
name: "request without namespace, field selector with metadata.namespace",
recursive: true,
fieldSelector: fields.ParseSelectorOrDie("metadata.namespace=namespace"),
},
{
name: "request without namespace, field selector with spec.nodename",
recursive: true,
fieldSelector: fields.ParseSelectorOrDie("spec.nodeName=node"),
indexFields: []string{"spec.nodeName"},
expectIndex: "f:spec.nodeName",
},
{
name: "request without namespace, field selector with spec.nodename to filter out",
recursive: true,
fieldSelector: fields.ParseSelectorOrDie("spec.nodeName!=node"),
indexFields: []string{"spec.nodeName"},
},
{
name: "request with namespace, without field selector",
requestedNamespace: "namespace",
recursive: true,
fieldSelector: fields.Everything(),
},
{
name: "request with namespace, field selector with matched metadata.namespace",
requestedNamespace: "namespace",
recursive: true,
fieldSelector: fields.ParseSelectorOrDie("metadata.namespace=namespace"),
},
{
name: "request with namespace, field selector with non-matched metadata.namespace",
requestedNamespace: "namespace",
recursive: true,
fieldSelector: fields.ParseSelectorOrDie("metadata.namespace=namespace"),
},
{
name: "request with namespace, field selector with spec.nodename",
requestedNamespace: "namespace",
recursive: true,
fieldSelector: fields.ParseSelectorOrDie("spec.nodeName=node"),
indexFields: []string{"spec.nodeName"},
expectIndex: "f:spec.nodeName",
},
{
name: "request with namespace, field selector with spec.nodename to filter out",
requestedNamespace: "namespace",
recursive: true,
fieldSelector: fields.ParseSelectorOrDie("spec.nodeName!=node"),
indexFields: []string{"spec.nodeName"},
},
{
name: "request without namespace, field selector with metadata.name",
recursive: true,
fieldSelector: fields.ParseSelectorOrDie("metadata.name=name"),
},
{
name: "request without namespace, field selector with metadata.name and metadata.namespace",
recursive: true,
fieldSelector: fields.SelectorFromSet(fields.Set{
"metadata.name": "name",
"metadata.namespace": "namespace",
}),
},
{
name: "request without namespace, field selector with metadata.name and spec.nodeName",
recursive: true,
fieldSelector: fields.SelectorFromSet(fields.Set{
"metadata.name": "name",
"spec.nodeName": "node",
}),
indexFields: []string{"spec.nodeName"},
expectIndex: "f:spec.nodeName",
},
{
name: "request without namespace, field selector with metadata.name, and with spec.nodeName to filter out watch",
recursive: true,
fieldSelector: fields.AndSelectors(
fields.ParseSelectorOrDie("spec.nodeName!=node"),
fields.SelectorFromSet(fields.Set{"metadata.name": "name"}),
),
indexFields: []string{"spec.nodeName"},
},
{
name: "request with namespace, with field selector metadata.name",
requestedNamespace: "namespace",
fieldSelector: fields.ParseSelectorOrDie("metadata.name=name"),
},
{
name: "request with namespace, with field selector metadata.name and metadata.namespace",
requestedNamespace: "namespace",
fieldSelector: fields.SelectorFromSet(fields.Set{
"metadata.name": "name",
"metadata.namespace": "namespace",
}),
},
{
name: "request with namespace, with field selector metadata.name, metadata.namespace and spec.nodename",
requestedNamespace: "namespace",
fieldSelector: fields.SelectorFromSet(fields.Set{
"metadata.name": "name",
"metadata.namespace": "namespace",
"spec.nodeName": "node",
}),
indexFields: []string{"spec.nodeName"},
},
{
name: "request with namespace, with field selector metadata.name, metadata.namespace, and with spec.nodename to filter out",
requestedNamespace: "namespace",
fieldSelector: fields.AndSelectors(
fields.ParseSelectorOrDie("spec.nodeName!=node"),
fields.SelectorFromSet(fields.Set{"metadata.name": "name", "metadata.namespace": "namespace"}),
),
indexFields: []string{"spec.nodeName"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pred := storagetesting.CreatePodPredicate(tt.fieldSelector, true, tt.indexFields)
_, _, usedIndex, err := cacher.listItems(ctx, 0, "/pods/"+tt.requestedNamespace, pred, tt.recursive)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if usedIndex != tt.expectIndex {
t.Errorf("Index doesn't match, expected: %q, got: %q", tt.expectIndex, usedIndex)
}
})
}
}

View File

@ -285,6 +285,11 @@ func TestListContinuationWithFilter(t *testing.T) {
storagetesting.RunTestListContinuationWithFilter(ctx, t, store, validation)
}
func TestNamespaceScopedList(t *testing.T) {
ctx, store, _ := testSetup(t)
storagetesting.RunTestNamespaceScopedList(ctx, t, store)
}
func compactStorage(etcdClient *clientv3.Client) storagetesting.Compaction {
return func(ctx context.Context, t *testing.T, resourceVersion string) {
versioner := storage.APIObjectVersioner{}

View File

@ -2761,3 +2761,403 @@ func RunTestListPaging(ctx context.Context, t *testing.T, store storage.Interfac
t.Errorf("unexpected items: %#v", names)
}
}
func RunTestNamespaceScopedList(ctx context.Context, t *testing.T, store storage.Interface) {
tests := []struct {
name string
requestedNamespace string
recursive bool
indexFields []string
fieldSelector func(namespace string) fields.Selector
inputPods func(namespace string) []example.Pod
expectPods func(namespace string) []example.Pod
}{
{
name: "request without namespace, without field selector",
recursive: true,
fieldSelector: func(namespace string) fields.Selector { return fields.Everything() },
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", "ns1"),
*baseNamespacedPod("foo2", "ns2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", "ns1"),
*baseNamespacedPod("foo2", "ns2"),
}
},
},
{
name: "request without namespace, field selector with metadata.namespace",
recursive: true,
fieldSelector: func(namespace string) fields.Selector {
return fields.SelectorFromSet(fields.Set{"metadata.namespace": namespace + "ns1"})
},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
}
},
},
{
name: "request without namespace, field selector with spec.nodename",
recursive: true,
fieldSelector: func(namespace string) fields.Selector {
return fields.ParseSelectorOrDie("spec.nodeName=bar1")
},
indexFields: []string{"spec.nodeName"},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo1", namespace+"ns1", "bar1"),
*baseNamespacedPodAssigned("foo2", namespace+"ns2", "bar2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo1", namespace+"ns1", "bar1"),
}
},
},
{
name: "request without namespace, field selector with spec.nodename to filter out",
recursive: true,
fieldSelector: func(namespace string) fields.Selector { return fields.ParseSelectorOrDie("spec.nodeName!=bar1") },
indexFields: []string{"spec.nodeName"},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo1", namespace+"ns1", "bar1"),
*baseNamespacedPodAssigned("foo2", namespace+"ns2", "bar2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo2", namespace+"ns2", "bar2"),
}
},
},
{
name: "request with namespace, without field selector",
requestedNamespace: "ns1",
recursive: true,
fieldSelector: func(namespace string) fields.Selector { return fields.Everything() },
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
*baseNamespacedPod("foo2", namespace+"ns1"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo2", namespace+"ns1"),
}
},
},
{
name: "request with namespace, field selector with matched metadata.namespace",
requestedNamespace: "ns1",
recursive: true,
fieldSelector: func(namespace string) fields.Selector {
return fields.SelectorFromSet(fields.Set{"metadata.namespace": namespace + "ns1"})
},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
}
},
},
{
name: "request with namespace, field selector with non-matched metadata.namespace",
requestedNamespace: "ns1",
recursive: true,
fieldSelector: func(namespace string) fields.Selector {
return fields.SelectorFromSet(fields.Set{"metadata.namespace": namespace + "ns2"})
},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
*baseNamespacedPodUpdated("foo2", namespace+"ns1"),
*baseNamespacedPodUpdated("foo2", namespace+"ns2"),
}
},
expectPods: func(namespace string) []example.Pod { return []example.Pod{} },
},
{
name: "request with namespace, field selector with spec.nodename",
requestedNamespace: "ns1",
recursive: true,
fieldSelector: func(namespace string) fields.Selector { return fields.ParseSelectorOrDie("spec.nodeName=bar2") },
indexFields: []string{"spec.nodeName"},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo1", namespace+"ns1", "bar1"),
*baseNamespacedPodAssigned("foo1", namespace+"ns2", "bar2"),
*baseNamespacedPodAssigned("foo2", namespace+"ns1", "bar2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo2", namespace+"ns1", "bar2"),
}
},
},
{
name: "request with namespace, field selector with spec.nodename to filter out",
requestedNamespace: "ns2",
recursive: true,
fieldSelector: func(namespace string) fields.Selector { return fields.ParseSelectorOrDie("spec.nodeName!=bar1") },
indexFields: []string{"spec.nodeName"},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
*baseNamespacedPodAssigned("foo3", namespace+"ns2", "bar1"),
*baseNamespacedPodAssigned("foo2", namespace+"ns2", "bar2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns2"),
*baseNamespacedPodAssigned("foo2", namespace+"ns2", "bar2"),
}
},
},
{
name: "request without namespace, field selector with metadata.name",
recursive: true,
fieldSelector: func(namespace string) fields.Selector { return fields.ParseSelectorOrDie("metadata.name=foo1") },
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
*baseNamespacedPod("foo2", namespace+"ns1"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
}
},
},
{
name: "request without namespace, field selector with metadata.name and metadata.namespace",
recursive: true,
fieldSelector: func(namespace string) fields.Selector {
return fields.SelectorFromSet(fields.Set{
"metadata.name": "foo1",
"metadata.namespace": namespace + "ns1",
})
},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo2", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
}
},
},
{
name: "request without namespace, field selector with metadata.name and spec.nodeName",
recursive: true,
fieldSelector: func(namespace string) fields.Selector {
return fields.SelectorFromSet(fields.Set{
"metadata.name": "foo1",
"spec.nodeName": "bar1",
})
},
indexFields: []string{"spec.nodeName"},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo1", namespace+"ns1", "bar1"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo1", namespace+"ns1", "bar1"),
}
},
},
{
name: "request without namespace, field selector with metadata.name, and with spec.nodeName to filter out watch",
recursive: true,
fieldSelector: func(namespace string) fields.Selector {
return fields.AndSelectors(
fields.ParseSelectorOrDie("spec.nodeName!=bar1"),
fields.SelectorFromSet(fields.Set{"metadata.name": "foo1"}),
)
},
indexFields: []string{"spec.nodeName"},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo2", namespace+"ns1"),
*baseNamespacedPodAssigned("foo1", namespace+"ns2", "bar1"),
*baseNamespacedPodAssigned("foo2", namespace+"ns2", "bar1"),
*baseNamespacedPodAssigned("foo1", namespace+"ns3", "bar2"),
*baseNamespacedPodAssigned("foo2", namespace+"ns3", "bar2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPodAssigned("foo1", namespace+"ns3", "bar2"),
}
},
},
{
name: "request with namespace, with field selector metadata.name",
requestedNamespace: "ns1",
fieldSelector: func(namespace string) fields.Selector { return fields.ParseSelectorOrDie("metadata.name=foo1") },
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo2", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
*baseNamespacedPod("foo2", namespace+"ns2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
}
},
},
{
name: "request with namespace, with field selector metadata.name and metadata.namespace",
requestedNamespace: "ns1",
fieldSelector: func(namespace string) fields.Selector {
return fields.SelectorFromSet(fields.Set{
"metadata.name": "foo1",
"metadata.namespace": namespace + "ns1",
})
},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo2", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
*baseNamespacedPod("foo2", namespace+"ns2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
}
},
},
{
name: "request with namespace, with field selector metadata.name, metadata.namespace and spec.nodename",
requestedNamespace: "ns2",
fieldSelector: func(namespace string) fields.Selector {
return fields.SelectorFromSet(fields.Set{
"metadata.name": "foo2",
"metadata.namespace": namespace + "ns2",
"spec.nodeName": "bar1",
})
},
indexFields: []string{"spec.nodeName"},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo2", namespace+"ns1"),
*baseNamespacedPod("foo1", namespace+"ns2"),
*baseNamespacedPodAssigned("foo2", namespace+"ns2", "bar1"),
*baseNamespacedPodAssigned("foo1", namespace+"ns3", "bar1"),
*baseNamespacedPodAssigned("foo2", namespace+"ns3", "bar1"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo2", namespace+"ns2", "bar1"),
}
},
},
{
name: "request with namespace, with field selector metadata.name, metadata.namespace, and with spec.nodename to filter out",
requestedNamespace: "ns2",
fieldSelector: func(namespace string) fields.Selector {
return fields.AndSelectors(
fields.ParseSelectorOrDie("spec.nodeName!=bar2"),
fields.SelectorFromSet(fields.Set{"metadata.name": "foo1", "metadata.namespace": namespace + "ns2"}),
)
},
indexFields: []string{"spec.nodeName"},
inputPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPod("foo1", namespace+"ns1"),
*baseNamespacedPod("foo2", namespace+"ns1"),
*baseNamespacedPodAssigned("foo1", namespace+"ns2", "bar1"),
*baseNamespacedPodAssigned("foo2", namespace+"ns2", "bar2"),
*baseNamespacedPodAssigned("foo1", namespace+"ns3", "bar2"),
}
},
expectPods: func(namespace string) []example.Pod {
return []example.Pod{
*baseNamespacedPodAssigned("foo1", namespace+"ns2", "bar1"),
}
},
},
}
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
podNames := map[string]struct{}{}
namespace := fmt.Sprintf("t%d-", i)
for _, pod := range tt.inputPods(namespace) {
out := &example.Pod{}
key := computePodKey(&pod)
podNames[key] = struct{}{}
err := store.Create(ctx, key, &pod, out, 0)
if err != nil {
t.Fatalf("GuaranteedUpdate failed: %v", err)
}
}
opts := storage.ListOptions{
ResourceVersion: "",
Predicate: CreatePodPredicate(tt.fieldSelector(namespace), true, tt.indexFields),
Recursive: true,
}
listOut := &example.PodList{}
path := "/pods/"
if tt.requestedNamespace != "" {
path += namespace + tt.requestedNamespace
}
if err := store.GetList(ctx, path, opts, listOut); err != nil {
t.Errorf("Unexpected error: %v", err)
}
i := 0
for _, pod := range listOut.Items {
if _, found := podNames[computePodKey(&pod)]; !found {
continue
}
pod.ResourceVersion = ""
listOut.Items[i] = pod
i++
}
listOut.Items = listOut.Items[:i]
expectNoDiff(t, "incorrect list pods", tt.expectPods(namespace), listOut.Items)
})
}
}

View File

@ -30,6 +30,8 @@ import (
"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/api/meta"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
@ -389,3 +391,42 @@ func (s sortablePodList) Less(i, j int) bool {
func (s sortablePodList) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func CreatePodPredicate(field fields.Selector, namespaceScoped bool, indexField []string) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: labels.Everything(),
Field: field,
GetAttrs: determinePodGetAttrFunc(namespaceScoped, indexField),
IndexFields: indexField,
}
}
func determinePodGetAttrFunc(namespaceScoped bool, indexField []string) storage.AttrFunc {
if indexField != nil {
if namespaceScoped {
return namespacedScopedNodeNameAttrFunc
}
return clusterScopedNodeNameAttrFunc
}
if namespaceScoped {
return storage.DefaultNamespaceScopedAttr
}
return storage.DefaultClusterScopedAttr
}
func namespacedScopedNodeNameAttrFunc(obj runtime.Object) (labels.Set, fields.Set, error) {
pod := obj.(*example.Pod)
return nil, fields.Set{
"spec.nodeName": pod.Spec.NodeName,
"metadata.name": pod.ObjectMeta.Name,
"metadata.namespace": pod.ObjectMeta.Namespace,
}, nil
}
func clusterScopedNodeNameAttrFunc(obj runtime.Object) (labels.Set, fields.Set, error) {
pod := obj.(*example.Pod)
return nil, fields.Set{
"spec.nodeName": pod.Spec.NodeName,
"metadata.name": pod.ObjectMeta.Name,
}, nil
}

View File

@ -674,7 +674,7 @@ func RunTestClusterScopedWatch(ctx context.Context, t *testing.T, store storage.
watchKey += "/" + tt.requestedName
}
predicate := createPodPredicate(tt.fieldSelector, false, tt.indexFields)
predicate := CreatePodPredicate(tt.fieldSelector, false, tt.indexFields)
list := &example.PodList{}
opts := storage.ListOptions{
@ -988,7 +988,7 @@ func RunTestNamespaceScopedWatch(ctx context.Context, t *testing.T, store storag
}
}
predicate := createPodPredicate(tt.fieldSelector, true, tt.indexFields)
predicate := CreatePodPredicate(tt.fieldSelector, true, tt.indexFields)
list := &example.PodList{}
opts := storage.ListOptions{
@ -1656,45 +1656,6 @@ type testWatchStruct struct {
watchType watch.EventType
}
func createPodPredicate(field fields.Selector, namespaceScoped bool, indexField []string) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: labels.Everything(),
Field: field,
GetAttrs: determinePodGetAttrFunc(namespaceScoped, indexField),
IndexFields: indexField,
}
}
func determinePodGetAttrFunc(namespaceScoped bool, indexField []string) storage.AttrFunc {
if indexField != nil {
if namespaceScoped {
return namespacedScopedNodeNameAttrFunc
}
return clusterScopedNodeNameAttrFunc
}
if namespaceScoped {
return storage.DefaultNamespaceScopedAttr
}
return storage.DefaultClusterScopedAttr
}
func namespacedScopedNodeNameAttrFunc(obj runtime.Object) (labels.Set, fields.Set, error) {
pod := obj.(*example.Pod)
return nil, fields.Set{
"spec.nodeName": pod.Spec.NodeName,
"metadata.name": pod.ObjectMeta.Name,
"metadata.namespace": pod.ObjectMeta.Namespace,
}, nil
}
func clusterScopedNodeNameAttrFunc(obj runtime.Object) (labels.Set, fields.Set, error) {
pod := obj.(*example.Pod)
return nil, fields.Set{
"spec.nodeName": pod.Spec.NodeName,
"metadata.name": pod.ObjectMeta.Name,
}, nil
}
func basePod(podName string) *example.Pod {
return baseNamespacedPod(podName, "")
}