1
16
17 package endpointslice
18
19 import (
20 "context"
21 "reflect"
22 "testing"
23 "time"
24
25 corev1 "k8s.io/api/core/v1"
26 discovery "k8s.io/api/discovery/v1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/util/intstr"
29 "k8s.io/apimachinery/pkg/util/wait"
30 "k8s.io/client-go/informers"
31 clientset "k8s.io/client-go/kubernetes"
32 kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
33 "k8s.io/kubernetes/pkg/controller/endpointslice"
34 "k8s.io/kubernetes/test/integration/framework"
35 "k8s.io/kubernetes/test/utils/ktesting"
36 utilpointer "k8s.io/utils/pointer"
37 )
38
39
40
41 func TestEndpointSliceTerminating(t *testing.T) {
42 testcases := []struct {
43 name string
44 podStatus corev1.PodStatus
45 expectedEndpoints []discovery.Endpoint
46 }{
47 {
48 name: "ready terminating pods",
49 podStatus: corev1.PodStatus{
50 Phase: corev1.PodRunning,
51 Conditions: []corev1.PodCondition{
52 {
53 Type: corev1.PodReady,
54 Status: corev1.ConditionTrue,
55 },
56 },
57 PodIP: "10.0.0.1",
58 PodIPs: []corev1.PodIP{
59 {
60 IP: "10.0.0.1",
61 },
62 },
63 },
64 expectedEndpoints: []discovery.Endpoint{
65 {
66 Addresses: []string{"10.0.0.1"},
67 Conditions: discovery.EndpointConditions{
68 Ready: utilpointer.BoolPtr(false),
69 Serving: utilpointer.BoolPtr(true),
70 Terminating: utilpointer.BoolPtr(true),
71 },
72 },
73 },
74 },
75 {
76 name: "not ready terminating pods",
77 podStatus: corev1.PodStatus{
78 Phase: corev1.PodRunning,
79 Conditions: []corev1.PodCondition{
80 {
81 Type: corev1.PodReady,
82 Status: corev1.ConditionFalse,
83 },
84 },
85 PodIP: "10.0.0.1",
86 PodIPs: []corev1.PodIP{
87 {
88 IP: "10.0.0.1",
89 },
90 },
91 },
92 expectedEndpoints: []discovery.Endpoint{
93 {
94 Addresses: []string{"10.0.0.1"},
95 Conditions: discovery.EndpointConditions{
96 Ready: utilpointer.BoolPtr(false),
97 Serving: utilpointer.BoolPtr(false),
98 Terminating: utilpointer.BoolPtr(true),
99 },
100 },
101 },
102 },
103 }
104
105 for _, testcase := range testcases {
106 t.Run(testcase.name, func(t *testing.T) {
107
108 server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--disable-admission-plugins=ServiceAccount"}, framework.SharedEtcd())
109 defer server.TearDownFn()
110
111 client, err := clientset.NewForConfig(server.ClientConfig)
112 if err != nil {
113 t.Fatalf("Error creating clientset: %v", err)
114 }
115
116 resyncPeriod := 12 * time.Hour
117 informers := informers.NewSharedInformerFactory(client, resyncPeriod)
118
119 tCtx := ktesting.Init(t)
120 epsController := endpointslice.NewController(
121 tCtx,
122 informers.Core().V1().Pods(),
123 informers.Core().V1().Services(),
124 informers.Core().V1().Nodes(),
125 informers.Discovery().V1().EndpointSlices(),
126 int32(100),
127 client,
128 1*time.Second)
129
130
131 informers.Start(tCtx.Done())
132 go epsController.Run(tCtx, 1)
133
134
135 ns := framework.CreateNamespaceOrDie(client, "test-endpoints-terminating", t)
136 defer framework.DeleteNamespaceOrDie(client, ns, t)
137
138 node := &corev1.Node{
139 ObjectMeta: metav1.ObjectMeta{
140 Name: "fake-node",
141 },
142 }
143
144 _, err = client.CoreV1().Nodes().Create(context.TODO(), node, metav1.CreateOptions{})
145 if err != nil {
146 t.Fatalf("Failed to create test node: %v", err)
147 }
148
149 svc := &corev1.Service{
150 ObjectMeta: metav1.ObjectMeta{
151 Name: "test-service",
152 Namespace: ns.Name,
153 Labels: map[string]string{
154 "foo": "bar",
155 },
156 },
157 Spec: corev1.ServiceSpec{
158 Selector: map[string]string{
159 "foo": "bar",
160 },
161 Ports: []corev1.ServicePort{
162 {Name: "port-443", Port: 443, Protocol: "TCP", TargetPort: intstr.FromInt32(443)},
163 },
164 },
165 }
166
167 _, err = client.CoreV1().Services(ns.Name).Create(context.TODO(), svc, metav1.CreateOptions{})
168 if err != nil {
169 t.Fatalf("Failed to create test Service: %v", err)
170 }
171
172 pod := &corev1.Pod{
173 ObjectMeta: metav1.ObjectMeta{
174 Name: "test-pod",
175 Labels: map[string]string{
176 "foo": "bar",
177 },
178 },
179 Spec: corev1.PodSpec{
180 NodeName: "fake-node",
181 Containers: []corev1.Container{
182 {
183 Name: "fakename",
184 Image: "fakeimage",
185 Ports: []corev1.ContainerPort{
186 {
187 Name: "port-443",
188 ContainerPort: 443,
189 },
190 },
191 },
192 },
193 },
194 }
195
196 pod, err = client.CoreV1().Pods(ns.Name).Create(context.TODO(), pod, metav1.CreateOptions{})
197 if err != nil {
198 t.Fatalf("Failed to create test ready pod: %v", err)
199 }
200
201 pod.Status = testcase.podStatus
202 _, err = client.CoreV1().Pods(ns.Name).UpdateStatus(context.TODO(), pod, metav1.UpdateOptions{})
203 if err != nil {
204 t.Fatalf("Failed to update status for test ready pod: %v", err)
205 }
206
207
208 err = wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
209 esList, err := client.DiscoveryV1().EndpointSlices(ns.Name).List(context.TODO(), metav1.ListOptions{
210 LabelSelector: discovery.LabelServiceName + "=" + svc.Name,
211 })
212
213 if err != nil {
214 return false, err
215 }
216
217 if len(esList.Items) == 0 {
218 return false, nil
219 }
220
221 numEndpoints := 0
222 for _, slice := range esList.Items {
223 numEndpoints += len(slice.Endpoints)
224 }
225
226 if numEndpoints > 0 {
227 return true, nil
228 }
229
230 return false, nil
231 })
232 if err != nil {
233 t.Errorf("Error waiting for endpoint slices: %v", err)
234 }
235
236
237 err = client.CoreV1().Pods(ns.Name).Delete(context.TODO(), pod.Name, metav1.DeleteOptions{})
238 if err != nil {
239 t.Fatalf("Failed to delete pod in terminating state: %v", err)
240 }
241
242
243
244 var endpoints []discovery.Endpoint
245 err = wait.PollImmediate(1*time.Second, 10*time.Second, func() (bool, error) {
246 esList, err := client.DiscoveryV1().EndpointSlices(ns.Name).List(context.TODO(), metav1.ListOptions{
247 LabelSelector: discovery.LabelServiceName + "=" + svc.Name,
248 })
249
250 if err != nil {
251 return false, err
252 }
253
254 if len(esList.Items) == 0 {
255 return false, nil
256 }
257
258 endpoints = esList.Items[0].Endpoints
259 if len(endpoints) == 0 && len(testcase.expectedEndpoints) == 0 {
260 return true, nil
261 }
262
263 if len(endpoints) != len(testcase.expectedEndpoints) {
264 return false, nil
265 }
266
267 if !reflect.DeepEqual(endpoints[0].Addresses, testcase.expectedEndpoints[0].Addresses) {
268 return false, nil
269 }
270
271 if !reflect.DeepEqual(endpoints[0].Conditions, testcase.expectedEndpoints[0].Conditions) {
272 return false, nil
273 }
274
275 return true, nil
276 })
277 if err != nil {
278 t.Logf("actual endpoints: %v", endpoints)
279 t.Logf("expected endpoints: %v", testcase.expectedEndpoints)
280 t.Errorf("unexpected endpoints: %v", err)
281 }
282 })
283 }
284 }
285
View as plain text