mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 18:54:06 +00:00
Merge pull request #121906 from ahutsunshine/namespace-indexer
support namespace indexer for namespaced resources like pods
This commit is contained in:
commit
2eb2a62e15
@ -975,6 +975,12 @@ const (
|
||||
// will not graduate or be enabled by default in future Kubernetes
|
||||
// releases.
|
||||
UserNamespacesPodSecurityStandards featuregate.Feature = "UserNamespacesPodSecurityStandards"
|
||||
|
||||
// owner: @ahutsunshine
|
||||
// beta: v1.29
|
||||
//
|
||||
// Allows namespace indexer for namespace scope resources in apiserver cache to accelerate list operations.
|
||||
StorageNamespaceIndex featuregate.Feature = "StorageNamespaceIndex"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -1283,4 +1289,6 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
// features that enable backwards compatibility but are scheduled to be removed
|
||||
// ...
|
||||
HPAScaleToZero: {Default: false, PreRelease: featuregate.Alpha},
|
||||
|
||||
StorageNamespaceIndex: {Default: true, PreRelease: featuregate.Beta},
|
||||
}
|
||||
|
@ -289,11 +289,15 @@ func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||
|
||||
// MatchPod returns a generic matcher for a given label and field selector.
|
||||
func MatchPod(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
|
||||
var indexFields = []string{"spec.nodeName"}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StorageNamespaceIndex) {
|
||||
indexFields = append(indexFields, "metadata.namespace")
|
||||
}
|
||||
return storage.SelectionPredicate{
|
||||
Label: label,
|
||||
Field: field,
|
||||
GetAttrs: GetAttrs,
|
||||
IndexFields: []string{"spec.nodeName"},
|
||||
IndexFields: indexFields,
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,11 +315,24 @@ func NodeNameIndexFunc(obj interface{}) ([]string, error) {
|
||||
return []string{pod.Spec.NodeName}, nil
|
||||
}
|
||||
|
||||
// NamespaceIndexFunc return value name of given object.
|
||||
func NamespaceIndexFunc(obj interface{}) ([]string, error) {
|
||||
pod, ok := obj.(*api.Pod)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("not a pod")
|
||||
}
|
||||
return []string{pod.Namespace}, nil
|
||||
}
|
||||
|
||||
// Indexers returns the indexers for pod storage.
|
||||
func Indexers() *cache.Indexers {
|
||||
return &cache.Indexers{
|
||||
var indexers = cache.Indexers{
|
||||
storage.FieldIndex("spec.nodeName"): NodeNameIndexFunc,
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.StorageNamespaceIndex) {
|
||||
indexers[storage.FieldIndex("metadata.namespace")] = NamespaceIndexFunc
|
||||
}
|
||||
return &indexers
|
||||
}
|
||||
|
||||
// ToSelectableFields returns a field set that represents the object
|
||||
|
@ -746,7 +746,7 @@ func (c *Cacher) listItems(ctx context.Context, listRV uint64, key string, pred
|
||||
}
|
||||
return nil, readResourceVersion, "", nil
|
||||
}
|
||||
return c.watchCache.WaitUntilFreshAndList(ctx, listRV, pred.MatcherIndex())
|
||||
return c.watchCache.WaitUntilFreshAndList(ctx, listRV, pred.MatcherIndex(ctx))
|
||||
}
|
||||
|
||||
// GetList implements storage.Interface
|
||||
|
@ -17,10 +17,13 @@ limitations under the License.
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
// AttrFunc returns label and field sets and the uninitialized flag for List or Watch to match.
|
||||
@ -145,11 +148,16 @@ func (s *SelectionPredicate) Empty() bool {
|
||||
// For any index defined by IndexFields, if a matcher can match only (a subset)
|
||||
// of objects that return <value> for a given index, a pair (<index name>, <value>)
|
||||
// wil be returned.
|
||||
func (s *SelectionPredicate) MatcherIndex() []MatchValue {
|
||||
func (s *SelectionPredicate) MatcherIndex(ctx context.Context) []MatchValue {
|
||||
var result []MatchValue
|
||||
for _, field := range s.IndexFields {
|
||||
if value, ok := s.Field.RequiresExactMatch(field); ok {
|
||||
result = append(result, MatchValue{IndexName: FieldIndex(field), Value: value})
|
||||
} else if field == "metadata.namespace" {
|
||||
// list pods in the namespace. i.e. /api/v1/namespaces/default/pods
|
||||
if namespace, isNamespaceScope := isNamespaceScopedRequest(ctx); isNamespaceScope {
|
||||
result = append(result, MatchValue{IndexName: FieldIndex(field), Value: namespace})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, label := range s.IndexLabels {
|
||||
@ -160,6 +168,14 @@ func (s *SelectionPredicate) MatcherIndex() []MatchValue {
|
||||
return result
|
||||
}
|
||||
|
||||
func isNamespaceScopedRequest(ctx context.Context) (string, bool) {
|
||||
re, _ := request.RequestInfoFrom(ctx)
|
||||
if re == nil || len(re.Namespace) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return re.Namespace, true
|
||||
}
|
||||
|
||||
// LabelIndex add prefix for label index.
|
||||
func LabelIndex(label string) string {
|
||||
return "l:" + label
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
@ -25,6 +26,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
type Ignored struct {
|
||||
@ -127,12 +129,14 @@ func TestSelectionPredicateMatcherIndex(t *testing.T) {
|
||||
indexLabels []string
|
||||
indexFields []string
|
||||
expected []MatchValue
|
||||
ctx context.Context
|
||||
}{
|
||||
"Match nil": {
|
||||
labelSelector: "name=foo",
|
||||
fieldSelector: "uid=12345",
|
||||
indexLabels: []string{"bar"},
|
||||
indexFields: []string{},
|
||||
ctx: context.Background(),
|
||||
expected: nil,
|
||||
},
|
||||
"Match field": {
|
||||
@ -140,13 +144,83 @@ func TestSelectionPredicateMatcherIndex(t *testing.T) {
|
||||
fieldSelector: "uid=12345",
|
||||
indexLabels: []string{},
|
||||
indexFields: []string{"uid"},
|
||||
ctx: context.Background(),
|
||||
expected: []MatchValue{{IndexName: FieldIndex("uid"), Value: "12345"}},
|
||||
},
|
||||
"Match field for listing namespace pods without metadata.namespace field selector": {
|
||||
labelSelector: "",
|
||||
fieldSelector: "",
|
||||
indexLabels: []string{},
|
||||
indexFields: []string{"metadata.namespace"},
|
||||
ctx: request.WithRequestInfo(context.Background(), &request.RequestInfo{
|
||||
IsResourceRequest: true,
|
||||
Path: "/api/v1/namespaces/default/pods",
|
||||
Verb: "list",
|
||||
APIPrefix: "api",
|
||||
APIGroup: "",
|
||||
APIVersion: "v1",
|
||||
Namespace: "default",
|
||||
Resource: "pods",
|
||||
}),
|
||||
expected: []MatchValue{{IndexName: FieldIndex("metadata.namespace"), Value: "default"}},
|
||||
},
|
||||
"Match field for listing namespace pods with metadata.namespace field selector": {
|
||||
labelSelector: "",
|
||||
fieldSelector: "metadata.namespace=kube-system",
|
||||
indexLabels: []string{},
|
||||
indexFields: []string{"metadata.namespace"},
|
||||
ctx: request.WithRequestInfo(context.Background(), &request.RequestInfo{
|
||||
IsResourceRequest: true,
|
||||
Path: "/api/v1/namespaces/default/pods",
|
||||
Verb: "list",
|
||||
APIPrefix: "api",
|
||||
APIGroup: "",
|
||||
APIVersion: "v1",
|
||||
Namespace: "default",
|
||||
Resource: "pods",
|
||||
}),
|
||||
expected: []MatchValue{{IndexName: FieldIndex("metadata.namespace"), Value: "kube-system"}},
|
||||
},
|
||||
"Match field for listing all pods without metadata.namespace field selector": {
|
||||
labelSelector: "",
|
||||
fieldSelector: "",
|
||||
indexLabels: []string{},
|
||||
indexFields: []string{"metadata.namespace"},
|
||||
ctx: request.WithRequestInfo(context.Background(), &request.RequestInfo{
|
||||
IsResourceRequest: true,
|
||||
Path: "/api/v1/pods",
|
||||
Verb: "list",
|
||||
APIPrefix: "api",
|
||||
APIGroup: "",
|
||||
APIVersion: "v1",
|
||||
Namespace: "",
|
||||
Resource: "pods",
|
||||
}),
|
||||
expected: nil,
|
||||
},
|
||||
"Match field for listing all pods with metadata.namespace field selector": {
|
||||
labelSelector: "",
|
||||
fieldSelector: "metadata.namespace=default",
|
||||
indexLabels: []string{},
|
||||
indexFields: []string{"metadata.namespace"},
|
||||
ctx: request.WithRequestInfo(context.Background(), &request.RequestInfo{
|
||||
IsResourceRequest: true,
|
||||
Path: "/api/v1/pods",
|
||||
Verb: "list",
|
||||
APIPrefix: "api",
|
||||
APIGroup: "",
|
||||
APIVersion: "v1",
|
||||
Namespace: "default",
|
||||
Resource: "pods",
|
||||
}),
|
||||
expected: []MatchValue{{IndexName: FieldIndex("metadata.namespace"), Value: "default"}},
|
||||
},
|
||||
"Match label": {
|
||||
labelSelector: "name=foo",
|
||||
fieldSelector: "uid=12345",
|
||||
indexLabels: []string{"name"},
|
||||
indexFields: []string{},
|
||||
ctx: context.Background(),
|
||||
expected: []MatchValue{{IndexName: LabelIndex("name"), Value: "foo"}},
|
||||
},
|
||||
"Match field and label": {
|
||||
@ -154,6 +228,7 @@ func TestSelectionPredicateMatcherIndex(t *testing.T) {
|
||||
fieldSelector: "uid=12345",
|
||||
indexLabels: []string{"name"},
|
||||
indexFields: []string{"uid"},
|
||||
ctx: context.Background(),
|
||||
expected: []MatchValue{{IndexName: FieldIndex("uid"), Value: "12345"}, {IndexName: LabelIndex("name"), Value: "foo"}},
|
||||
},
|
||||
"Negative match field and label": {
|
||||
@ -161,6 +236,7 @@ func TestSelectionPredicateMatcherIndex(t *testing.T) {
|
||||
fieldSelector: "uid!=12345",
|
||||
indexLabels: []string{"name"},
|
||||
indexFields: []string{"uid"},
|
||||
ctx: context.Background(),
|
||||
expected: nil,
|
||||
},
|
||||
"Negative match field and match label": {
|
||||
@ -168,6 +244,7 @@ func TestSelectionPredicateMatcherIndex(t *testing.T) {
|
||||
fieldSelector: "uid!=12345",
|
||||
indexLabels: []string{"name"},
|
||||
indexFields: []string{"uid"},
|
||||
ctx: context.Background(),
|
||||
expected: []MatchValue{{IndexName: LabelIndex("name"), Value: "foo"}},
|
||||
},
|
||||
"Negative match label and match field": {
|
||||
@ -175,6 +252,7 @@ func TestSelectionPredicateMatcherIndex(t *testing.T) {
|
||||
fieldSelector: "uid=12345",
|
||||
indexLabels: []string{"name"},
|
||||
indexFields: []string{"uid"},
|
||||
ctx: context.Background(),
|
||||
expected: []MatchValue{{IndexName: FieldIndex("uid"), Value: "12345"}},
|
||||
},
|
||||
}
|
||||
@ -194,7 +272,7 @@ func TestSelectionPredicateMatcherIndex(t *testing.T) {
|
||||
IndexLabels: testCase.indexLabels,
|
||||
IndexFields: testCase.indexFields,
|
||||
}
|
||||
actual := sp.MatcherIndex()
|
||||
actual := sp.MatcherIndex(testCase.ctx)
|
||||
if !reflect.DeepEqual(testCase.expected, actual) {
|
||||
t.Errorf("%v: expected %v, got %v", name, testCase.expected, actual)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user