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  }