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  }