k8s.io/apiserver@v0.31.1/pkg/storage/selection_predicate_test.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package storage
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"k8s.io/apimachinery/pkg/fields"
    26  	"k8s.io/apimachinery/pkg/labels"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/apiserver/pkg/endpoints/request"
    30  )
    31  
    32  type Ignored struct {
    33  	ID string
    34  }
    35  
    36  func (obj *Ignored) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
    37  func (obj *Ignored) DeepCopyObject() runtime.Object {
    38  	panic("Ignored does not support DeepCopy")
    39  }
    40  
    41  func TestSelectionPredicate(t *testing.T) {
    42  	table := map[string]struct {
    43  		labelSelector, fieldSelector string
    44  		labels                       labels.Set
    45  		fields                       fields.Set
    46  		err                          error
    47  		shouldMatch                  bool
    48  		matchSingleKey               string
    49  	}{
    50  		"A": {
    51  			labelSelector: "name=foo",
    52  			fieldSelector: "uid=12345",
    53  			labels:        labels.Set{"name": "foo"},
    54  			fields:        fields.Set{"uid": "12345"},
    55  			shouldMatch:   true,
    56  		},
    57  		"B": {
    58  			labelSelector: "name=foo",
    59  			fieldSelector: "uid=12345",
    60  			labels:        labels.Set{"name": "foo"},
    61  			fields:        fields.Set{},
    62  			shouldMatch:   false,
    63  		},
    64  		"C": {
    65  			labelSelector: "name=foo",
    66  			fieldSelector: "uid=12345",
    67  			labels:        labels.Set{},
    68  			fields:        fields.Set{"uid": "12345"},
    69  			shouldMatch:   false,
    70  		},
    71  		"D": {
    72  			fieldSelector:  "metadata.name=12345",
    73  			labels:         labels.Set{},
    74  			fields:         fields.Set{"metadata.name": "12345"},
    75  			shouldMatch:    true,
    76  			matchSingleKey: "12345",
    77  		},
    78  		"error": {
    79  			labelSelector: "name=foo",
    80  			fieldSelector: "uid=12345",
    81  			err:           errors.New("maybe this is a 'wrong object type' error"),
    82  			shouldMatch:   false,
    83  		},
    84  	}
    85  
    86  	for name, item := range table {
    87  		parsedLabel, err := labels.Parse(item.labelSelector)
    88  		if err != nil {
    89  			panic(err)
    90  		}
    91  		parsedField, err := fields.ParseSelector(item.fieldSelector)
    92  		if err != nil {
    93  			panic(err)
    94  		}
    95  		sp := &SelectionPredicate{
    96  			Label: parsedLabel,
    97  			Field: parsedField,
    98  			GetAttrs: func(runtime.Object) (label labels.Set, field fields.Set, err error) {
    99  				return item.labels, item.fields, item.err
   100  			},
   101  		}
   102  		got, err := sp.Matches(&Ignored{})
   103  		if e, a := item.err, err; e != a {
   104  			t.Errorf("%v: expected %v, got %v", name, e, a)
   105  			continue
   106  		}
   107  		if e, a := item.shouldMatch, got; e != a {
   108  			t.Errorf("%v: expected %v, got %v", name, e, a)
   109  		}
   110  		got = sp.MatchesObjectAttributes(item.labels, item.fields)
   111  		if e, a := item.shouldMatch, got; e != a {
   112  			t.Errorf("%v: expected %v, got %v", name, e, a)
   113  		}
   114  		if key := item.matchSingleKey; key != "" {
   115  			got, ok := sp.MatchesSingle()
   116  			if !ok {
   117  				t.Errorf("%v: expected single match", name)
   118  			}
   119  			if e, a := key, got; e != a {
   120  				t.Errorf("%v: expected %v, got %v", name, e, a)
   121  			}
   122  		}
   123  	}
   124  }
   125  
   126  func TestSelectionPredicateMatcherIndex(t *testing.T) {
   127  	testCases := map[string]struct {
   128  		labelSelector, fieldSelector string
   129  		indexLabels                  []string
   130  		indexFields                  []string
   131  		expected                     []MatchValue
   132  		ctx                          context.Context
   133  	}{
   134  		"Match nil": {
   135  			labelSelector: "name=foo",
   136  			fieldSelector: "uid=12345",
   137  			indexLabels:   []string{"bar"},
   138  			indexFields:   []string{},
   139  			ctx:           context.Background(),
   140  			expected:      nil,
   141  		},
   142  		"Match field": {
   143  			labelSelector: "name=foo",
   144  			fieldSelector: "uid=12345",
   145  			indexLabels:   []string{},
   146  			indexFields:   []string{"uid"},
   147  			ctx:           context.Background(),
   148  			expected:      []MatchValue{{IndexName: FieldIndex("uid"), Value: "12345"}},
   149  		},
   150  		"Match field for listing namespace pods without metadata.namespace field selector": {
   151  			labelSelector: "",
   152  			fieldSelector: "",
   153  			indexLabels:   []string{},
   154  			indexFields:   []string{"metadata.namespace"},
   155  			ctx: request.WithRequestInfo(context.Background(), &request.RequestInfo{
   156  				IsResourceRequest: true,
   157  				Path:              "/api/v1/namespaces/default/pods",
   158  				Verb:              "list",
   159  				APIPrefix:         "api",
   160  				APIGroup:          "",
   161  				APIVersion:        "v1",
   162  				Namespace:         "default",
   163  				Resource:          "pods",
   164  			}),
   165  			expected: []MatchValue{{IndexName: FieldIndex("metadata.namespace"), Value: "default"}},
   166  		},
   167  		"Match field for listing namespace pods with metadata.namespace field selector": {
   168  			labelSelector: "",
   169  			fieldSelector: "metadata.namespace=kube-system",
   170  			indexLabels:   []string{},
   171  			indexFields:   []string{"metadata.namespace"},
   172  			ctx: request.WithRequestInfo(context.Background(), &request.RequestInfo{
   173  				IsResourceRequest: true,
   174  				Path:              "/api/v1/namespaces/default/pods",
   175  				Verb:              "list",
   176  				APIPrefix:         "api",
   177  				APIGroup:          "",
   178  				APIVersion:        "v1",
   179  				Namespace:         "default",
   180  				Resource:          "pods",
   181  			}),
   182  			expected: []MatchValue{{IndexName: FieldIndex("metadata.namespace"), Value: "kube-system"}},
   183  		},
   184  		"Match field for listing all pods without metadata.namespace field selector": {
   185  			labelSelector: "",
   186  			fieldSelector: "",
   187  			indexLabels:   []string{},
   188  			indexFields:   []string{"metadata.namespace"},
   189  			ctx: request.WithRequestInfo(context.Background(), &request.RequestInfo{
   190  				IsResourceRequest: true,
   191  				Path:              "/api/v1/pods",
   192  				Verb:              "list",
   193  				APIPrefix:         "api",
   194  				APIGroup:          "",
   195  				APIVersion:        "v1",
   196  				Namespace:         "",
   197  				Resource:          "pods",
   198  			}),
   199  			expected: nil,
   200  		},
   201  		"Match field for listing all pods with metadata.namespace field selector": {
   202  			labelSelector: "",
   203  			fieldSelector: "metadata.namespace=default",
   204  			indexLabels:   []string{},
   205  			indexFields:   []string{"metadata.namespace"},
   206  			ctx: request.WithRequestInfo(context.Background(), &request.RequestInfo{
   207  				IsResourceRequest: true,
   208  				Path:              "/api/v1/pods",
   209  				Verb:              "list",
   210  				APIPrefix:         "api",
   211  				APIGroup:          "",
   212  				APIVersion:        "v1",
   213  				Namespace:         "default",
   214  				Resource:          "pods",
   215  			}),
   216  			expected: []MatchValue{{IndexName: FieldIndex("metadata.namespace"), Value: "default"}},
   217  		},
   218  		"Match label": {
   219  			labelSelector: "name=foo",
   220  			fieldSelector: "uid=12345",
   221  			indexLabels:   []string{"name"},
   222  			indexFields:   []string{},
   223  			ctx:           context.Background(),
   224  			expected:      []MatchValue{{IndexName: LabelIndex("name"), Value: "foo"}},
   225  		},
   226  		"Match field and label": {
   227  			labelSelector: "name=foo",
   228  			fieldSelector: "uid=12345",
   229  			indexLabels:   []string{"name"},
   230  			indexFields:   []string{"uid"},
   231  			ctx:           context.Background(),
   232  			expected:      []MatchValue{{IndexName: FieldIndex("uid"), Value: "12345"}, {IndexName: LabelIndex("name"), Value: "foo"}},
   233  		},
   234  		"Negative match field and label": {
   235  			labelSelector: "name!=foo",
   236  			fieldSelector: "uid!=12345",
   237  			indexLabels:   []string{"name"},
   238  			indexFields:   []string{"uid"},
   239  			ctx:           context.Background(),
   240  			expected:      nil,
   241  		},
   242  		"Negative match field and match label": {
   243  			labelSelector: "name=foo",
   244  			fieldSelector: "uid!=12345",
   245  			indexLabels:   []string{"name"},
   246  			indexFields:   []string{"uid"},
   247  			ctx:           context.Background(),
   248  			expected:      []MatchValue{{IndexName: LabelIndex("name"), Value: "foo"}},
   249  		},
   250  		"Negative match label and match field": {
   251  			labelSelector: "name!=foo",
   252  			fieldSelector: "uid=12345",
   253  			indexLabels:   []string{"name"},
   254  			indexFields:   []string{"uid"},
   255  			ctx:           context.Background(),
   256  			expected:      []MatchValue{{IndexName: FieldIndex("uid"), Value: "12345"}},
   257  		},
   258  	}
   259  	for name, testCase := range testCases {
   260  		parsedLabel, err := labels.Parse(testCase.labelSelector)
   261  		if err != nil {
   262  			panic(err)
   263  		}
   264  		parsedField, err := fields.ParseSelector(testCase.fieldSelector)
   265  		if err != nil {
   266  			panic(err)
   267  		}
   268  
   269  		sp := &SelectionPredicate{
   270  			Label:       parsedLabel,
   271  			Field:       parsedField,
   272  			IndexLabels: testCase.indexLabels,
   273  			IndexFields: testCase.indexFields,
   274  		}
   275  		actual := sp.MatcherIndex(testCase.ctx)
   276  		if !reflect.DeepEqual(testCase.expected, actual) {
   277  			t.Errorf("%v: expected %v, got %v", name, testCase.expected, actual)
   278  		}
   279  	}
   280  }