k8s.io/apiserver@v0.31.1/pkg/util/flowcontrol/watch_tracker_test.go (about) 1 /* 2 Copyright 2021 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 flowcontrol 18 19 import ( 20 "context" 21 "net/http" 22 "net/url" 23 "testing" 24 25 "k8s.io/apimachinery/pkg/util/sets" 26 "k8s.io/apiserver/pkg/endpoints/request" 27 ) 28 29 func httpRequest(method, path, rawQuery string) *http.Request { 30 return &http.Request{ 31 Method: method, 32 URL: &url.URL{ 33 Path: path, 34 RawQuery: rawQuery, 35 }, 36 } 37 } 38 39 func newWatchIdentifier(apiGroup, resource, namespace, name string) *watchIdentifier { 40 return &watchIdentifier{ 41 apiGroup: apiGroup, 42 resource: resource, 43 namespace: namespace, 44 name: name, 45 } 46 } 47 48 func TestRegisterWatch(t *testing.T) { 49 testCases := []struct { 50 name string 51 request *http.Request 52 expected *watchIdentifier 53 }{ 54 { 55 name: "watch all objects", 56 request: httpRequest("GET", "/api/v1/pods", "watch=true"), 57 expected: newWatchIdentifier("", "pods", "", ""), 58 }, 59 { 60 name: "list all objects", 61 request: httpRequest("GET", "/api/v1/pods", ""), 62 expected: nil, 63 }, 64 { 65 name: "watch namespace-scoped objects", 66 request: httpRequest("GET", "/api/v1/namespaces/foo/pods", "watch=true"), 67 expected: newWatchIdentifier("", "pods", "foo", ""), 68 }, 69 { 70 name: "watch single object", 71 request: httpRequest("GET", "/api/v1/namespaces/foo/pods", "watch=true&fieldSelector=metadata.name=mypod"), 72 expected: newWatchIdentifier("", "pods", "foo", "mypod"), 73 }, 74 { 75 name: "watch single cluster-scoped object", 76 request: httpRequest("GET", "/api/v1/namespaces", "watch=true&fieldSelector=metadata.name=myns"), 77 expected: newWatchIdentifier("", "namespaces", "", "myns"), 78 }, 79 { 80 name: "watch all objects from api-group", 81 request: httpRequest("GET", "/apis/group/v1/pods", "watch=true"), 82 expected: newWatchIdentifier("group", "pods", "", ""), 83 }, 84 { 85 name: "watch namespace-scoped objects", 86 request: httpRequest("GET", "/apis/group/v1/namespaces/foo/pods", "watch=true"), 87 expected: newWatchIdentifier("group", "pods", "foo", ""), 88 }, 89 { 90 name: "watch single object", 91 request: httpRequest("GET", "/apis/group/v1/namespaces/foo/pods", "watch=true&fieldSelector=metadata.name=mypod"), 92 expected: newWatchIdentifier("group", "pods", "foo", "mypod"), 93 }, 94 { 95 name: "watch indexed object", 96 request: httpRequest("GET", "/apis/group/v1/namespaces/foo/pods", "watch=true&fieldSelector=spec.nodeName="), 97 expected: newWatchIdentifier("group", "pods", "foo", ""), 98 }, 99 } 100 101 requestInfoFactory := &request.RequestInfoFactory{ 102 APIPrefixes: sets.NewString("api", "apis"), 103 GrouplessAPIPrefixes: sets.NewString("api"), 104 } 105 106 for _, testCase := range testCases { 107 t.Run(testCase.name, func(t *testing.T) { 108 watchTracker := &watchTracker{ 109 indexes: getBuiltinIndexes(), 110 watchCount: make(map[watchIdentifier]int), 111 } 112 113 requestInfo, err := requestInfoFactory.NewRequestInfo(testCase.request) 114 if err != nil { 115 t.Fatalf("unexpected error from requestInfo creation: %#v", err) 116 } 117 ctx := request.WithRequestInfo(context.Background(), requestInfo) 118 r := testCase.request.WithContext(ctx) 119 120 forget := watchTracker.RegisterWatch(r) 121 if testCase.expected == nil { 122 if forget != nil { 123 t.Errorf("unexpected watch registered: %#v", watchTracker.watchCount) 124 } 125 return 126 } 127 128 if forget == nil { 129 t.Errorf("watch should be registered, got: %v", forget) 130 return 131 } 132 if count := watchTracker.watchCount[*testCase.expected]; count != 1 { 133 t.Errorf("unexpected watch registered: %#v", watchTracker.watchCount) 134 } 135 forget() 136 if count := watchTracker.watchCount[*testCase.expected]; count != 0 { 137 t.Errorf("forget should unregister the watch: %#v", watchTracker.watchCount) 138 } 139 }) 140 } 141 } 142 143 func TestGetInterestedWatchCount(t *testing.T) { 144 watchTracker := NewWatchTracker() 145 146 registeredWatches := []*http.Request{ 147 httpRequest("GET", "api/v1/pods", "watch=true"), 148 httpRequest("GET", "api/v1/namespaces/foo/pods", "watch=true"), 149 httpRequest("GET", "api/v1/namespaces/foo/pods", "watch=true&fieldSelector=metadata.name=mypod"), 150 httpRequest("GET", "api/v1/namespaces/bar/pods", "watch=true&fieldSelector=metadata.name=mypod"), 151 httpRequest("GET", "apis/group/v1/namespaces/foo/pods", "watch=true"), 152 httpRequest("GET", "apis/group/v1/namespaces/bar/pods", "watch=true&fieldSelector=metadata.name=mypod"), 153 } 154 requestInfoFactory := &request.RequestInfoFactory{ 155 APIPrefixes: sets.NewString("api", "apis"), 156 GrouplessAPIPrefixes: sets.NewString("api"), 157 } 158 for _, req := range registeredWatches { 159 requestInfo, err := requestInfoFactory.NewRequestInfo(req) 160 if err != nil { 161 t.Fatalf("unexpected error from requestInfo creation: %#v", err) 162 } 163 r := req.WithContext(request.WithRequestInfo(context.Background(), requestInfo)) 164 if forget := watchTracker.RegisterWatch(r); forget == nil { 165 t.Errorf("watch wasn't registered: %#v", requestInfo) 166 } 167 } 168 169 testCases := []struct { 170 name string 171 request *http.Request 172 expected int 173 }{ 174 { 175 name: "pod creation in foo namespace", 176 request: httpRequest("POST", "/api/v1/namespaces/foo/pods", ""), 177 expected: 2, 178 }, 179 { 180 name: "mypod update in foo namespace", 181 request: httpRequest("PUT", "/api/v1/namespaces/foo/pods/mypod", ""), 182 expected: 3, 183 }, 184 { 185 name: "mypod patch in foo namespace", 186 request: httpRequest("PATCH", "/api/v1/namespaces/foo/pods/mypod", ""), 187 expected: 3, 188 }, 189 { 190 name: "mypod deletion in foo namespace", 191 request: httpRequest("DELETE", "/api/v1/namespaces/foo/pods/mypod", ""), 192 expected: 3, 193 }, 194 { 195 name: "otherpod update in foo namespace", 196 request: httpRequest("PUT", "/api/v1/namespaces/foo/pods/otherpod", ""), 197 expected: 2, 198 }, 199 { 200 name: "mypod get in foo namespace", 201 request: httpRequest("GET", "/api/v1/namespaces/foo/pods/mypod", ""), 202 expected: 0, 203 }, 204 { 205 name: "pods list in foo namespace", 206 request: httpRequest("GET", "/api/v1/namespaces/foo/pods", ""), 207 expected: 0, 208 }, 209 { 210 name: "pods watch in foo namespace", 211 request: httpRequest("GET", "/api/v1/namespaces/foo/pods", "watch=true"), 212 expected: 0, 213 }, 214 { 215 name: "pods proxy in foo namespace", 216 request: httpRequest("GET", "/api/v1/proxy/namespaces/foo/pods/mypod", ""), 217 expected: 0, 218 }, 219 { 220 name: "pod creation in bar namespace", 221 request: httpRequest("POST", "/api/v1/namespaces/bar/pods", ""), 222 expected: 1, 223 }, 224 { 225 name: "mypod update in bar namespace", 226 request: httpRequest("PUT", "/api/v1/namespaces/bar/pods/mypod", ""), 227 expected: 2, 228 }, 229 { 230 name: "mypod update in foo namespace in group group", 231 request: httpRequest("PUT", "/apis/group/v1/namespaces/foo/pods/mypod", ""), 232 expected: 1, 233 }, 234 { 235 name: "otherpod update in foo namespace in group group", 236 request: httpRequest("PUT", "/apis/group/v1/namespaces/foo/pods/otherpod", ""), 237 expected: 1, 238 }, 239 { 240 name: "mypod update in var namespace in group group", 241 request: httpRequest("PUT", "/apis/group/v1/namespaces/bar/pods/mypod", ""), 242 expected: 1, 243 }, 244 { 245 name: "otherpod update in bar namespace in group group", 246 request: httpRequest("PUT", "/apis/group/v1/namespaces/bar/pods/otherpod", ""), 247 expected: 0, 248 }, 249 } 250 251 for _, testCase := range testCases { 252 t.Run(testCase.name, func(t *testing.T) { 253 requestInfo, err := requestInfoFactory.NewRequestInfo(testCase.request) 254 if err != nil { 255 t.Fatalf("unexpected error from requestInfo creation: %#v", err) 256 } 257 258 count := watchTracker.GetInterestedWatchCount(requestInfo) 259 if count != testCase.expected { 260 t.Errorf("unexpected interested watch count: %d, expected %d", count, testCase.expected) 261 } 262 }) 263 } 264 265 } 266 267 func TestGetInterestedWatchCountWithIndex(t *testing.T) { 268 watchTracker := NewWatchTracker() 269 270 registeredWatches := []*http.Request{ 271 httpRequest("GET", "api/v1/pods", "watch=true"), 272 httpRequest("GET", "api/v1/namespaces/foo/pods", "watch=true"), 273 httpRequest("GET", "api/v1/namespaces/foo/pods", "watch=true&fieldSelector=metadata.name=mypod"), 274 httpRequest("GET", "api/v1/namespaces/foo/pods", "watch=true&fieldSelector=spec.nodeName="), 275 // The watches below will be ignored due to index. 276 httpRequest("GET", "api/v1/namespaces/foo/pods", "watch=true&fieldSelector=spec.nodeName=node1"), 277 httpRequest("GET", "api/v1/namespaces/foo/pods", "watch=true&fieldSelector=spec.nodeName=node2"), 278 } 279 requestInfoFactory := &request.RequestInfoFactory{ 280 APIPrefixes: sets.NewString("api", "apis"), 281 GrouplessAPIPrefixes: sets.NewString("api"), 282 } 283 for _, req := range registeredWatches { 284 requestInfo, err := requestInfoFactory.NewRequestInfo(req) 285 if err != nil { 286 t.Fatalf("unexpected error from requestInfo creation: %#v", err) 287 } 288 r := req.WithContext(request.WithRequestInfo(context.Background(), requestInfo)) 289 if forget := watchTracker.RegisterWatch(r); forget == nil { 290 t.Errorf("watch wasn't registered: %#v", requestInfo) 291 } 292 } 293 294 testCases := []struct { 295 name string 296 request *http.Request 297 expected int 298 }{ 299 { 300 name: "pod creation in foo namespace", 301 request: httpRequest("POST", "/api/v1/namespaces/foo/pods", ""), 302 expected: 3, 303 }, 304 { 305 name: "mypod update in foo namespace", 306 request: httpRequest("PUT", "/api/v1/namespaces/foo/pods/mypod", ""), 307 expected: 4, 308 }, 309 { 310 name: "mypod patch in foo namespace", 311 request: httpRequest("PATCH", "/api/v1/namespaces/foo/pods/mypod", ""), 312 expected: 4, 313 }, 314 { 315 name: "mypod deletion in foo namespace", 316 request: httpRequest("DELETE", "/api/v1/namespaces/foo/pods/mypod", ""), 317 expected: 4, 318 }, 319 } 320 321 for _, testCase := range testCases { 322 t.Run(testCase.name, func(t *testing.T) { 323 requestInfo, err := requestInfoFactory.NewRequestInfo(testCase.request) 324 if err != nil { 325 t.Fatalf("unexpected error from requestInfo creation: %#v", err) 326 } 327 328 count := watchTracker.GetInterestedWatchCount(requestInfo) 329 if count != testCase.expected { 330 t.Errorf("unexpected interested watch count: %d, expected %d", count, testCase.expected) 331 } 332 }) 333 } 334 335 }