github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/k8s/owner_fetcher_test.go (about) 1 package k8s 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "golang.org/x/sync/errgroup" 9 appsv1 "k8s.io/api/apps/v1" 10 v1 "k8s.io/api/core/v1" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 ) 13 14 func TestVisitOneParent(t *testing.T) { 15 kCli := NewFakeK8sClient(t) 16 ov := NewOwnerFetcher(context.Background(), kCli) 17 18 pod, rs := fakeOneParentChain() 19 kCli.Inject(NewK8sEntity(rs)) 20 21 tree, err := ov.OwnerTreeOf(context.Background(), NewK8sEntity(pod)) 22 assert.NoError(t, err) 23 assert.Equal(t, `Pod:pod-a 24 ReplicaSet:rs-a`, tree.String()) 25 } 26 27 func TestVisitTwoParentsEnsureListCaching(t *testing.T) { 28 kCli := NewFakeK8sClient(t) 29 ov := NewOwnerFetcher(context.Background(), kCli) 30 31 pod, rs, dep := fakeTwoParentChain() 32 kCli.Inject(NewK8sEntity(rs), NewK8sEntity(dep)) 33 34 tree, err := ov.OwnerTreeOf(context.Background(), NewK8sEntity(pod)) 35 assert.NoError(t, err) 36 assert.Equal(t, `Pod:pod-a 37 ReplicaSet:rs-a 38 Deployment:dep-a`, tree.String()) 39 assert.Equal(t, 2, kCli.listCallCount) 40 assert.Equal(t, 0, kCli.getByReferenceCallCount) 41 } 42 43 func TestVisitTwoParentsNoList(t *testing.T) { 44 kCli := NewFakeK8sClient(t) 45 kCli.listReturnsEmpty = true 46 ov := NewOwnerFetcher(context.Background(), kCli) 47 48 pod, rs, dep := fakeTwoParentChain() 49 kCli.Inject(NewK8sEntity(rs), NewK8sEntity(dep)) 50 51 tree, err := ov.OwnerTreeOf(context.Background(), NewK8sEntity(pod)) 52 assert.NoError(t, err) 53 assert.Equal(t, `Pod:pod-a 54 ReplicaSet:rs-a 55 Deployment:dep-a`, tree.String()) 56 assert.Equal(t, 2, kCli.listCallCount) 57 assert.Equal(t, 2, kCli.getByReferenceCallCount) 58 } 59 60 func TestOwnerFetcherParallelism(t *testing.T) { 61 kCli := NewFakeK8sClient(t) 62 kCli.listReturnsEmpty = true 63 ov := NewOwnerFetcher(context.Background(), kCli) 64 65 pod, rs := fakeOneParentChain() 66 kCli.Inject(NewK8sEntity(rs)) 67 68 count := 30 69 g, ctx := errgroup.WithContext(context.Background()) 70 for i := 0; i < count; i++ { 71 g.Go(func() error { 72 _, err := ov.OwnerTreeOf(ctx, NewK8sEntity(pod)) 73 return err 74 }) 75 } 76 77 err := g.Wait() 78 assert.NoError(t, err) 79 assert.Equal(t, 1, kCli.getByReferenceCallCount) 80 } 81 82 func TestCircular(t *testing.T) { 83 kCli := NewFakeK8sClient(t) 84 kCli.listReturnsEmpty = true 85 ov := NewOwnerFetcher(context.Background(), kCli) 86 87 pod1, pod2, pod3 := fakeCircularReference() 88 kCli.Inject(NewK8sEntity(pod2), NewK8sEntity(pod3)) 89 90 tree, err := ov.OwnerTreeOf(context.Background(), NewK8sEntity(pod1)) 91 assert.NoError(t, err) 92 assert.Equal(t, `Pod:pod-a 93 Pod:pod-b 94 Pod:pod-c`, tree.String()) 95 } 96 97 func fakeOneParentChain() (*v1.Pod, *appsv1.ReplicaSet) { 98 pod := &v1.Pod{ 99 ObjectMeta: metav1.ObjectMeta{ 100 Name: "pod-a", 101 UID: "pod-a-uid", 102 Namespace: "default", 103 OwnerReferences: []metav1.OwnerReference{ 104 { 105 APIVersion: "apps/v1", 106 Kind: "ReplicaSet", 107 Name: "rs-a", 108 UID: "rs-a-uid", 109 }, 110 }, 111 }, 112 } 113 rs := &appsv1.ReplicaSet{ 114 ObjectMeta: metav1.ObjectMeta{ 115 Name: "rs-a", 116 UID: "rs-a-uid", 117 Namespace: "default", 118 }, 119 } 120 return pod, rs 121 } 122 123 func fakeTwoParentChain() (*v1.Pod, *appsv1.ReplicaSet, *appsv1.Deployment) { 124 pod := &v1.Pod{ 125 ObjectMeta: metav1.ObjectMeta{ 126 Name: "pod-a", 127 UID: "pod-a-uid", 128 Namespace: "default", 129 OwnerReferences: []metav1.OwnerReference{ 130 { 131 APIVersion: "apps/v1", 132 Kind: "ReplicaSet", 133 Name: "rs-a", 134 UID: "rs-a-uid", 135 }, 136 }, 137 }, 138 } 139 rs := &appsv1.ReplicaSet{ 140 ObjectMeta: metav1.ObjectMeta{ 141 Name: "rs-a", 142 UID: "rs-a-uid", 143 Namespace: "default", 144 OwnerReferences: []metav1.OwnerReference{ 145 { 146 APIVersion: "apps/v1", 147 Kind: "Deployment", 148 Name: "dep-a", 149 UID: "dep-a-uid", 150 }, 151 }, 152 }, 153 } 154 dep := &appsv1.Deployment{ 155 ObjectMeta: metav1.ObjectMeta{ 156 Name: "dep-a", 157 UID: "dep-a-uid", 158 Namespace: "default", 159 }, 160 } 161 return pod, rs, dep 162 } 163 164 func fakeCircularReference() (*v1.Pod, *v1.Pod, *v1.Pod) { 165 pod1 := &v1.Pod{ 166 ObjectMeta: metav1.ObjectMeta{ 167 Name: "pod-a", 168 UID: "pod-a-uid", 169 Namespace: "default", 170 OwnerReferences: []metav1.OwnerReference{ 171 { 172 APIVersion: "v1", 173 Kind: "Pod", 174 Name: "pod-b", 175 UID: "pod-b-uid", 176 }, 177 }, 178 }, 179 } 180 pod2 := &v1.Pod{ 181 ObjectMeta: metav1.ObjectMeta{ 182 Name: "pod-b", 183 UID: "pod-b-uid", 184 Namespace: "default", 185 OwnerReferences: []metav1.OwnerReference{ 186 { 187 APIVersion: "v1", 188 Kind: "Pod", 189 Name: "pod-c", 190 UID: "pod-c-uid", 191 }, 192 }, 193 }, 194 } 195 pod3 := &v1.Pod{ 196 ObjectMeta: metav1.ObjectMeta{ 197 Name: "pod-c", 198 UID: "pod-c-uid", 199 Namespace: "default", 200 OwnerReferences: []metav1.OwnerReference{ 201 { 202 APIVersion: "v1", 203 Kind: "Pod", 204 Name: "pod-a", 205 UID: "pod-a-uid", 206 }, 207 }, 208 }, 209 } 210 211 return pod1, pod2, pod3 212 }