k8s.io/kubernetes@v1.29.3/pkg/client/tests/listwatch_test.go (about) 1 /* 2 Copyright 2015 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 tests 18 19 import ( 20 "net/http/httptest" 21 "net/url" 22 "testing" 23 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/fields" 26 "k8s.io/apimachinery/pkg/runtime/schema" 27 clientset "k8s.io/client-go/kubernetes" 28 restclient "k8s.io/client-go/rest" 29 . "k8s.io/client-go/tools/cache" 30 utiltesting "k8s.io/client-go/util/testing" 31 ) 32 33 func parseSelectorOrDie(s string) fields.Selector { 34 selector, err := fields.ParseSelector(s) 35 if err != nil { 36 panic(err) 37 } 38 return selector 39 } 40 41 // buildQueryValues is a convenience function for knowing if a namespace should be in a query param or not 42 func buildQueryValues(query url.Values) url.Values { 43 v := url.Values{} 44 for key, values := range query { 45 for _, value := range values { 46 v.Add(key, value) 47 } 48 } 49 50 return v 51 } 52 53 func buildLocation(resourcePath string, query url.Values) string { 54 return resourcePath + "?" + query.Encode() 55 } 56 57 func TestListWatchesCanList(t *testing.T) { 58 fieldSelectorQueryParamName := metav1.FieldSelectorQueryParam("v1") 59 table := []struct { 60 desc string 61 location string 62 resource string 63 namespace string 64 fieldSelector fields.Selector 65 }{ 66 { 67 desc: "node", 68 location: "/api/v1/nodes", 69 resource: "nodes", 70 namespace: metav1.NamespaceAll, 71 fieldSelector: parseSelectorOrDie(""), 72 }, 73 { 74 desc: "pod with 'assigned' field selector", 75 location: buildLocation( 76 "/api/v1/pods", 77 buildQueryValues(url.Values{fieldSelectorQueryParamName: []string{"spec.host="}})), 78 resource: "pods", 79 namespace: metav1.NamespaceAll, 80 fieldSelector: fields.Set{"spec.host": ""}.AsSelector(), 81 }, 82 { 83 desc: "pod in namespace 'foo'", 84 location: buildLocation( 85 "/api/v1/namespaces/foo/pods", 86 buildQueryValues(url.Values{fieldSelectorQueryParamName: []string{"spec.host="}})), 87 resource: "pods", 88 namespace: "foo", 89 fieldSelector: fields.Set{"spec.host": ""}.AsSelector(), 90 }, 91 } 92 for _, item := range table { 93 t.Run(item.desc, func(t *testing.T) { 94 handler := utiltesting.FakeHandler{ 95 StatusCode: 500, 96 ResponseBody: "", 97 T: t, 98 } 99 server := httptest.NewServer(&handler) 100 defer server.Close() 101 client := clientset.NewForConfigOrDie(&restclient.Config{Host: server.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}) 102 lw := NewListWatchFromClient(client.CoreV1().RESTClient(), item.resource, item.namespace, item.fieldSelector) 103 lw.DisableChunking = true 104 // This test merely tests that the correct request is made. 105 _, _ = lw.List(metav1.ListOptions{}) 106 handler.ValidateRequest(t, item.location, "GET", nil) 107 }) 108 } 109 } 110 111 func TestListWatchesCanWatch(t *testing.T) { 112 fieldSelectorQueryParamName := metav1.FieldSelectorQueryParam("v1") 113 table := []struct { 114 desc string 115 rv string 116 location string 117 resource string 118 namespace string 119 fieldSelector fields.Selector 120 }{ 121 { 122 desc: "node without rv", 123 location: buildLocation( 124 "/api/v1/nodes", 125 buildQueryValues(url.Values{"watch": []string{"true"}})), 126 rv: "", 127 resource: "nodes", 128 namespace: metav1.NamespaceAll, 129 fieldSelector: parseSelectorOrDie(""), 130 }, 131 { 132 desc: "node with rv", 133 location: buildLocation( 134 "/api/v1/nodes", 135 buildQueryValues(url.Values{"resourceVersion": []string{"42"}, "watch": []string{"true"}})), 136 rv: "42", 137 resource: "nodes", 138 namespace: metav1.NamespaceAll, 139 fieldSelector: parseSelectorOrDie(""), 140 }, 141 { 142 desc: "pod with 'assigned' field selector", 143 location: buildLocation( 144 "/api/v1/pods", 145 buildQueryValues(url.Values{fieldSelectorQueryParamName: []string{"spec.host="}, "resourceVersion": []string{"0"}, "watch": []string{"true"}})), 146 rv: "0", 147 resource: "pods", 148 namespace: metav1.NamespaceAll, 149 fieldSelector: fields.Set{"spec.host": ""}.AsSelector(), 150 }, 151 { 152 desc: "pod with namespace foo and assigned field selector", 153 location: buildLocation( 154 "/api/v1/namespaces/foo/pods", 155 buildQueryValues(url.Values{fieldSelectorQueryParamName: []string{"spec.host="}, "resourceVersion": []string{"0"}, "watch": []string{"true"}})), 156 rv: "0", 157 resource: "pods", 158 namespace: "foo", 159 fieldSelector: fields.Set{"spec.host": ""}.AsSelector(), 160 }, 161 } 162 163 for _, item := range table { 164 t.Run(item.desc, func(t *testing.T) { 165 handler := utiltesting.FakeHandler{ 166 StatusCode: 500, 167 ResponseBody: "", 168 T: t, 169 } 170 server := httptest.NewServer(&handler) 171 defer server.Close() 172 client := clientset.NewForConfigOrDie(&restclient.Config{Host: server.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}}) 173 lw := NewListWatchFromClient(client.CoreV1().RESTClient(), item.resource, item.namespace, item.fieldSelector) 174 // This test merely tests that the correct request is made. 175 _, _ = lw.Watch(metav1.ListOptions{ResourceVersion: item.rv}) 176 handler.ValidateRequest(t, item.location, "GET", nil) 177 }) 178 } 179 }