diff --git a/pkg/registry/core/pod/BUILD b/pkg/registry/core/pod/BUILD index 3bffa38009e..277bb410e98 100644 --- a/pkg/registry/core/pod/BUILD +++ b/pkg/registry/core/pod/BUILD @@ -30,10 +30,12 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/features:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//staging/src/k8s.io/apiserver/pkg/storage:go_default_library", "//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", + "//staging/src/k8s.io/client-go/tools/cache:go_default_library", ], ) @@ -54,6 +56,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library", + "//staging/src/k8s.io/client-go/tools/cache:go_default_library", ], ) diff --git a/pkg/registry/core/pod/storage/storage.go b/pkg/registry/core/pod/storage/storage.go index 68d49932711..27b63120574 100644 --- a/pkg/registry/core/pod/storage/storage.go +++ b/pkg/registry/core/pod/storage/storage.go @@ -87,6 +87,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGet RESTOptions: optsGetter, AttrFunc: registrypod.GetAttrs, TriggerFunc: map[string]storage.IndexerFunc{"spec.nodeName": registrypod.NodeNameTriggerFunc}, + Indexers: registrypod.Indexers(), } if err := store.CompleteWithOptions(options); err != nil { return PodStorage{}, err diff --git a/pkg/registry/core/pod/strategy.go b/pkg/registry/core/pod/strategy.go index ebd5f9426c6..afcf2113fff 100644 --- a/pkg/registry/core/pod/strategy.go +++ b/pkg/registry/core/pod/strategy.go @@ -34,10 +34,12 @@ import ( "k8s.io/apimachinery/pkg/types" utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/validation/field" + genericfeatures "k8s.io/apiserver/pkg/features" "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/client-go/tools/cache" "k8s.io/kubernetes/pkg/api/legacyscheme" podutil "k8s.io/kubernetes/pkg/api/pod" api "k8s.io/kubernetes/pkg/apis/core" @@ -200,6 +202,25 @@ func NodeNameTriggerFunc(obj runtime.Object) string { return obj.(*api.Pod).Spec.NodeName } +// NodeNameIndexFunc return value spec.nodename of given object. +func NodeNameIndexFunc(obj interface{}) ([]string, error) { + pod, ok := obj.(*api.Pod) + if !ok { + return nil, fmt.Errorf("not a pod") + } + return []string{pod.Spec.NodeName}, nil +} + +// Indexers returns the indexers for pod storage. +func Indexers() *cache.Indexers { + if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.SelectorIndex) { + return &cache.Indexers{ + storage.FieldIndex("spec.nodeName"): NodeNameIndexFunc, + } + } + return nil +} + // ToSelectableFields returns a field set that represents the object // TODO: fields are not labels, and the validation rules for them do not apply. func ToSelectableFields(pod *api.Pod) fields.Set { diff --git a/pkg/registry/core/pod/strategy_test.go b/pkg/registry/core/pod/strategy_test.go index c8b55df6df9..8c3cc48693c 100644 --- a/pkg/registry/core/pod/strategy_test.go +++ b/pkg/registry/core/pod/strategy_test.go @@ -18,6 +18,7 @@ package pod import ( "context" + "fmt" "net/http" "net/url" "reflect" @@ -31,6 +32,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/client-go/tools/cache" apitesting "k8s.io/kubernetes/pkg/api/testing" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/kubelet/client" @@ -629,3 +631,43 @@ var ( fakeSecureRoundTripper = fakeTransport{val: "secure"} fakeInsecureRoundTripper = fakeTransport{val: "insecure"} ) + +func TestPodIndexFunc(t *testing.T) { + tcs := []struct { + name string + indexFunc cache.IndexFunc + pod interface{} + expectedValue string + expectedErr error + }{ + { + name: "node name index", + indexFunc: NodeNameIndexFunc, + pod: &api.Pod{ + Spec: api.PodSpec{ + NodeName: "test-pod", + }, + }, + expectedValue: "test-pod", + expectedErr: nil, + }, + { + name: "not a pod failed", + indexFunc: NodeNameIndexFunc, + pod: "not a pod object", + expectedValue: "test-pod", + expectedErr: fmt.Errorf("not a pod"), + }, + } + + for _, tc := range tcs { + indexValues, err := tc.indexFunc(tc.pod) + if !reflect.DeepEqual(err, tc.expectedErr) { + t.Errorf("name %v, expected %v, got %v", tc.name, tc.expectedErr, err) + } + if err == nil && len(indexValues) != 1 && indexValues[0] != tc.expectedValue { + t.Errorf("name %v, expected %v, got %v", tc.name, tc.expectedValue, indexValues) + } + + } +} diff --git a/staging/src/k8s.io/apiserver/pkg/features/kube_features.go b/staging/src/k8s.io/apiserver/pkg/features/kube_features.go index 8f40751a38f..64bb3d57c13 100644 --- a/staging/src/k8s.io/apiserver/pkg/features/kube_features.go +++ b/staging/src/k8s.io/apiserver/pkg/features/kube_features.go @@ -140,6 +140,12 @@ const ( // // Deprecates and removes SelfLink from ObjectMeta and ListMeta. RemoveSelfLink featuregate.Feature = "RemoveSelfLink" + + // owner: @shaloulcy + // alpha: v1.18 + // + // Allows label and field based indexes in apiserver watch cache to accelerate list operations. + SelectorIndex featuregate.Feature = "SelectorIndex" ) func init() { @@ -165,4 +171,5 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS WatchBookmark: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, APIPriorityAndFairness: {Default: false, PreRelease: featuregate.Alpha}, RemoveSelfLink: {Default: false, PreRelease: featuregate.Alpha}, + SelectorIndex: {Default: false, PreRelease: featuregate.Alpha}, }