istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/kube/krt/join_test.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 krt_test 16 17 import ( 18 "testing" 19 20 "go.uber.org/atomic" 21 corev1 "k8s.io/api/core/v1" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 24 istio "istio.io/api/networking/v1alpha3" 25 istioclient "istio.io/client-go/pkg/apis/networking/v1alpha3" 26 "istio.io/istio/pkg/kube" 27 "istio.io/istio/pkg/kube/kclient" 28 "istio.io/istio/pkg/kube/kclient/clienttest" 29 "istio.io/istio/pkg/kube/krt" 30 "istio.io/istio/pkg/slices" 31 "istio.io/istio/pkg/test" 32 "istio.io/istio/pkg/test/util/assert" 33 ) 34 35 func TestJoinCollection(t *testing.T) { 36 c1 := krt.NewStatic[Named](nil) 37 c2 := krt.NewStatic[Named](nil) 38 c3 := krt.NewStatic[Named](nil) 39 j := krt.JoinCollection([]krt.Collection[Named]{c1.AsCollection(), c2.AsCollection(), c3.AsCollection()}) 40 last := atomic.NewString("") 41 j.Register(func(o krt.Event[Named]) { 42 last.Store(o.Latest().ResourceName()) 43 }) 44 assert.EventuallyEqual(t, last.Load, "") 45 c1.Set(&Named{"c1", "a"}) 46 assert.EventuallyEqual(t, last.Load, "c1/a") 47 48 c2.Set(&Named{"c2", "a"}) 49 assert.EventuallyEqual(t, last.Load, "c2/a") 50 51 c3.Set(&Named{"c3", "a"}) 52 assert.EventuallyEqual(t, last.Load, "c3/a") 53 54 c1.Set(&Named{"c1", "b"}) 55 assert.EventuallyEqual(t, last.Load, "c1/b") 56 // ordered by c1, c2, c3 57 sortf := func(a Named) string { 58 return a.ResourceName() 59 } 60 assert.Equal( 61 t, 62 slices.SortBy(j.List(), sortf), 63 slices.SortBy([]Named{ 64 {"c1", "b"}, 65 {"c2", "a"}, 66 {"c3", "a"}, 67 }, sortf), 68 ) 69 } 70 71 func TestCollectionJoin(t *testing.T) { 72 c := kube.NewFakeClient() 73 pods := krt.NewInformer[*corev1.Pod](c) 74 services := krt.NewInformer[*corev1.Service](c) 75 serviceEntries := krt.NewInformer[*istioclient.ServiceEntry](c) 76 c.RunAndWait(test.NewStop(t)) 77 pc := clienttest.Wrap(t, kclient.New[*corev1.Pod](c)) 78 sc := clienttest.Wrap(t, kclient.New[*corev1.Service](c)) 79 sec := clienttest.Wrap(t, kclient.New[*istioclient.ServiceEntry](c)) 80 SimplePods := SimplePodCollection(pods) 81 ExtraSimplePods := krt.NewStatic(&SimplePod{ 82 Named: Named{"namespace", "name-static"}, 83 Labeled: Labeled{map[string]string{"app": "foo"}}, 84 IP: "9.9.9.9", 85 }) 86 SimpleServices := SimpleServiceCollection(services) 87 SimpleServiceEntries := SimpleServiceCollectionFromEntries(serviceEntries) 88 AllServices := krt.JoinCollection([]krt.Collection[SimpleService]{SimpleServices, SimpleServiceEntries}) 89 AllPods := krt.JoinCollection([]krt.Collection[SimplePod]{SimplePods, ExtraSimplePods.AsCollection()}) 90 SimpleEndpoints := SimpleEndpointsCollection(AllPods, AllServices) 91 92 fetch := func() []SimpleEndpoint { 93 return slices.SortBy(SimpleEndpoints.List(), func(s SimpleEndpoint) string { return s.ResourceName() }) 94 } 95 96 assert.Equal(t, fetch(), nil) 97 pod := &corev1.Pod{ 98 ObjectMeta: metav1.ObjectMeta{ 99 Name: "name", 100 Namespace: "namespace", 101 Labels: map[string]string{"app": "foo"}, 102 }, 103 } 104 pc.Create(pod) 105 assert.Equal(t, fetch(), nil) 106 107 svc := &corev1.Service{ 108 ObjectMeta: metav1.ObjectMeta{ 109 Name: "svc", 110 Namespace: "namespace", 111 }, 112 Spec: corev1.ServiceSpec{Selector: map[string]string{"app": "foo"}}, 113 } 114 sc.Create(svc) 115 116 pod.Status = corev1.PodStatus{PodIP: "1.2.3.4"} 117 pc.UpdateStatus(pod) 118 119 assert.EventuallyEqual(t, fetch, []SimpleEndpoint{ 120 {pod.Name, svc.Name, pod.Namespace, "1.2.3.4"}, 121 {"name-static", svc.Name, pod.Namespace, "9.9.9.9"}, 122 }) 123 124 ExtraSimplePods.Set(nil) 125 assert.EventuallyEqual(t, fetch, []SimpleEndpoint{ 126 {pod.Name, svc.Name, pod.Namespace, "1.2.3.4"}, 127 }) 128 129 pod.Status.PodIP = "1.2.3.5" 130 pc.UpdateStatus(pod) 131 assert.EventuallyEqual(t, fetch, []SimpleEndpoint{{pod.Name, svc.Name, pod.Namespace, "1.2.3.5"}}) 132 133 pc.Delete(pod.Name, pod.Namespace) 134 assert.EventuallyEqual(t, fetch, nil) 135 136 pod2 := &corev1.Pod{ 137 ObjectMeta: metav1.ObjectMeta{ 138 Name: "name2", 139 Namespace: "namespace", 140 Labels: map[string]string{"app": "foo"}, 141 }, 142 Status: corev1.PodStatus{PodIP: "2.3.4.5"}, 143 } 144 pc.CreateOrUpdateStatus(pod) 145 pc.CreateOrUpdateStatus(pod2) 146 assert.EventuallyEqual(t, fetch, []SimpleEndpoint{ 147 {pod.Name, svc.Name, pod.Namespace, pod.Status.PodIP}, 148 {pod2.Name, svc.Name, pod2.Namespace, pod2.Status.PodIP}, 149 }) 150 151 se := &istioclient.ServiceEntry{ 152 ObjectMeta: metav1.ObjectMeta{ 153 Name: "svc-entry", 154 Namespace: "namespace", 155 }, 156 Spec: istio.ServiceEntry{WorkloadSelector: &istio.WorkloadSelector{Labels: map[string]string{"app": "foo"}}}, 157 } 158 sec.Create(se) 159 assert.EventuallyEqual(t, fetch, []SimpleEndpoint{ 160 {pod.Name, se.Name, pod.Namespace, pod.Status.PodIP}, 161 {pod2.Name, se.Name, pod2.Namespace, pod2.Status.PodIP}, 162 {pod.Name, svc.Name, pod.Namespace, pod.Status.PodIP}, 163 {pod2.Name, svc.Name, pod2.Namespace, pod2.Status.PodIP}, 164 }) 165 } 166 167 func TestCollectionJoinSync(t *testing.T) { 168 c := kube.NewFakeClient(&corev1.Pod{ 169 ObjectMeta: metav1.ObjectMeta{ 170 Name: "name", 171 Namespace: "namespace", 172 Labels: map[string]string{"app": "foo"}, 173 }, 174 Status: corev1.PodStatus{PodIP: "1.2.3.4"}, 175 }) 176 pods := krt.NewInformer[*corev1.Pod](c) 177 stop := test.NewStop(t) 178 c.RunAndWait(stop) 179 SimplePods := SimplePodCollection(pods) 180 ExtraSimplePods := krt.NewStatic(&SimplePod{ 181 Named: Named{"namespace", "name-static"}, 182 Labeled: Labeled{map[string]string{"app": "foo"}}, 183 IP: "9.9.9.9", 184 }) 185 AllPods := krt.JoinCollection([]krt.Collection[SimplePod]{SimplePods, ExtraSimplePods.AsCollection()}) 186 assert.Equal(t, AllPods.Synced().WaitUntilSynced(stop), true) 187 // Assert Equal -- not EventuallyEqual -- to ensure our WaitForCacheSync is proper 188 assert.Equal(t, fetcherSorted(AllPods)(), []SimplePod{ 189 {Named{"namespace", "name"}, NewLabeled(map[string]string{"app": "foo"}), "1.2.3.4"}, 190 {Named{"namespace", "name-static"}, NewLabeled(map[string]string{"app": "foo"}), "9.9.9.9"}, 191 }) 192 }