istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/kclient/clienttest/direct.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package clienttest 16 17 import ( 18 "context" 19 "reflect" 20 21 kerrors "k8s.io/apimachinery/pkg/api/errors" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 klabels "k8s.io/apimachinery/pkg/labels" 24 "k8s.io/apimachinery/pkg/runtime" 25 26 "istio.io/istio/pkg/config/schema/kubeclient" 27 "istio.io/istio/pkg/kube" 28 "istio.io/istio/pkg/kube/controllers" 29 "istio.io/istio/pkg/kube/kclient" 30 "istio.io/istio/pkg/test" 31 ) 32 33 type directClient[T controllers.Object, PT any, TL runtime.Object] struct { 34 kclient.Writer[T] 35 t test.Failer 36 client kube.Client 37 } 38 39 func (d *directClient[T, PT, TL]) Get(name, namespace string) T { 40 api := kubeclient.GetClient[T, TL](d.client, namespace) 41 res, err := api.Get(context.Background(), name, metav1.GetOptions{}) 42 if err != nil && !kerrors.IsNotFound(err) { 43 d.t.Fatalf("get: %v", err) 44 } 45 return res 46 } 47 48 func (d *directClient[T, PT, TL]) List(namespace string, selector klabels.Selector) []T { 49 api := kubeclient.GetClient[T, TL](d.client, namespace) 50 res, err := api.List(context.Background(), metav1.ListOptions{ 51 LabelSelector: selector.String(), 52 }) 53 if err != nil { 54 d.t.Fatalf("list: %v", err) 55 } 56 items := reflect.ValueOf(res).Elem().FieldByName("Items") 57 ret := make([]T, 0, items.Len()) 58 for i := 0; i < items.Len(); i++ { 59 itm := items.Index(i).Interface().(PT) 60 ret = append(ret, any(&itm).(T)) 61 } 62 return ret 63 } 64 65 var _ kclient.ReadWriter[controllers.Object] = &directClient[controllers.Object, any, controllers.Object]{} 66 67 // NewWriter returns a new client for the given type. 68 // Any errors will call t.Fatal. 69 func NewWriter[T controllers.ComparableObject](t test.Failer, c kube.Client) TestWriter[T] { 70 return TestWriter[T]{t: t, c: kclient.NewWriteClient[T](c)} 71 } 72 73 // NewDirectClient returns a new client for the given type. Reads are directly to the API server. 74 // Any errors will call t.Fatal. 75 // Typically, clienttest.WrapReadWriter should be used to simply wrap an existing client when testing an informer. 76 // However, NewDirectClient can be useful if we do not need/want an informer and need direct reads. 77 // Generic parameters represent the type with and without a pointer, and the list type. 78 // Example: NewDirectClient[*Pod, Pod, PodList] 79 func NewDirectClient[T controllers.ComparableObject, PT any, TL runtime.Object](t test.Failer, c kube.Client) TestClient[T] { 80 return WrapReadWriter[T](t, &directClient[T, PT, TL]{ 81 t: t, 82 client: c, 83 Writer: kclient.NewWriteClient[T](c), 84 }) 85 }