istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/kclient/clienttest/test_helpers.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 klabels "k8s.io/apimachinery/pkg/labels" 19 20 "istio.io/istio/pkg/kube/controllers" 21 "istio.io/istio/pkg/kube/kclient" 22 "istio.io/istio/pkg/slices" 23 "istio.io/istio/pkg/test" 24 "istio.io/istio/pkg/test/util/assert" 25 "istio.io/istio/pkg/util/sets" 26 ) 27 28 type TestClient[T controllers.Object] struct { 29 c kclient.ReadWriter[T] 30 t test.Failer 31 TestWriter[T] 32 } 33 34 type TestWriter[T controllers.Object] struct { 35 c kclient.Writer[T] 36 t test.Failer 37 } 38 39 func (t TestClient[T]) Get(name, namespace string) T { 40 return t.c.Get(name, namespace) 41 } 42 43 func (t TestClient[T]) List(namespace string, selector klabels.Selector) []T { 44 return t.c.List(namespace, selector) 45 } 46 47 func (t TestWriter[T]) Create(object T) T { 48 t.t.Helper() 49 res, err := t.c.Create(object) 50 if err != nil { 51 t.t.Fatalf("create %v/%v: %v", object.GetNamespace(), object.GetName(), err) 52 } 53 return res 54 } 55 56 func (t TestWriter[T]) Update(object T) T { 57 t.t.Helper() 58 res, err := t.c.Update(object) 59 if err != nil { 60 t.t.Fatalf("update %v/%v: %v", object.GetNamespace(), object.GetName(), err) 61 } 62 return res 63 } 64 65 func (t TestWriter[T]) UpdateStatus(object T) T { 66 t.t.Helper() 67 res, err := t.c.UpdateStatus(object) 68 if err != nil { 69 t.t.Fatalf("update status %v/%v: %v", object.GetNamespace(), object.GetName(), err) 70 } 71 return res 72 } 73 74 func (t TestWriter[T]) CreateOrUpdate(object T) T { 75 t.t.Helper() 76 res, err := kclient.CreateOrUpdate[T](t.c, object) 77 if err != nil { 78 t.t.Fatalf("createOrUpdate %v/%v: %v", object.GetNamespace(), object.GetName(), err) 79 } 80 return res 81 } 82 83 func (t TestWriter[T]) CreateOrUpdateStatus(object T) T { 84 t.t.Helper() 85 _, err := kclient.CreateOrUpdate(t.c, object) 86 if err != nil { 87 t.t.Fatalf("createOrUpdate %v/%v: %v", object.GetNamespace(), object.GetName(), err) 88 } 89 return t.UpdateStatus(object) 90 } 91 92 func (t TestWriter[T]) Delete(name, namespace string) { 93 t.t.Helper() 94 err := t.c.Delete(name, namespace) 95 if err != nil { 96 t.t.Fatalf("delete %v/%v: %v", namespace, name, err) 97 } 98 } 99 100 // WrapReadWriter returns a client that calls t.Fatal on errors. 101 // Reads may be cached or uncached, depending on the input client. 102 func WrapReadWriter[T controllers.Object](t test.Failer, c kclient.ReadWriter[T]) TestClient[T] { 103 return TestClient[T]{ 104 c: c, 105 t: t, 106 TestWriter: TestWriter[T]{ 107 c: c, 108 t: t, 109 }, 110 } 111 } 112 113 // Wrap returns a client that calls t.Fatal on errors. 114 // Reads may be cached or uncached, depending on the input client. 115 // Note: this is identical to WrapReadWriter but works around Go limitations, allowing calling w/o specifying 116 // generic parameters in the common case. 117 func Wrap[T controllers.Object](t test.Failer, c kclient.Client[T]) TestClient[T] { 118 return WrapReadWriter[T](t, c) 119 } 120 121 // TrackerHandler returns an object handler that records each event 122 func TrackerHandler(tracker *assert.Tracker[string]) controllers.EventHandler[controllers.Object] { 123 return controllers.EventHandler[controllers.Object]{ 124 AddFunc: func(obj controllers.Object) { 125 tracker.Record("add/" + obj.GetName()) 126 }, 127 UpdateFunc: func(oldObj, newObj controllers.Object) { 128 tracker.Record("update/" + newObj.GetName()) 129 }, 130 DeleteFunc: func(obj controllers.Object) { 131 tracker.Record("delete/" + obj.GetName()) 132 }, 133 } 134 } 135 136 func Names[T controllers.Object](list []T) sets.String { 137 return sets.New(slices.Map(list, func(a T) string { return a.GetName() })...) 138 }