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 }