istio

Форк
0
327 строк · 11.0 Кб
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 controller
16

17
import (
18
	"context"
19
	"fmt"
20
	"reflect"
21
	"testing"
22
	"time"
23

24
	v1 "k8s.io/api/core/v1"
25
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26
	"k8s.io/apimachinery/pkg/types"
27
	"k8s.io/client-go/kubernetes"
28

29
	"istio.io/istio/pilot/pkg/model"
30
	"istio.io/istio/pilot/pkg/serviceregistry/util/xdsfake"
31
	"istio.io/istio/pkg/config/labels"
32
	"istio.io/istio/pkg/kube/kclient/clienttest"
33
	"istio.io/istio/pkg/test"
34
	"istio.io/istio/pkg/test/util/assert"
35
	"istio.io/istio/pkg/test/util/retry"
36
	"istio.io/istio/pkg/util/sets"
37
)
38

39
// Prepare k8s. This can be used in multiple tests, to
40
// avoid duplicating creation, which can be tricky. It can be used with the fake or
41
// standalone apiserver.
42
func initTestEnv(t *testing.T, ki kubernetes.Interface, fx *xdsfake.Updater) {
43
	cleanup(ki)
44
	for _, n := range []string{"nsa", "nsb"} {
45
		_, err := ki.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{
46
			ObjectMeta: metav1.ObjectMeta{
47
				Name: n,
48
				Labels: map[string]string{
49
					"istio-injection": "enabled",
50
				},
51
			},
52
		}, metav1.CreateOptions{})
53
		if err != nil {
54
			t.Fatalf("failed creating test namespace: %v", err)
55
		}
56

57
		// K8S 1.10 also checks if service account exists
58
		_, err = ki.CoreV1().ServiceAccounts(n).Create(context.TODO(), &v1.ServiceAccount{
59
			ObjectMeta: metav1.ObjectMeta{
60
				Name: "default",
61
				Annotations: map[string]string{
62
					"kubernetes.io/enforce-mountable-secrets": "false",
63
				},
64
			},
65
			Secrets: []v1.ObjectReference{
66
				{
67
					Name: "default-token-2",
68
					UID:  "1",
69
				},
70
			},
71
		}, metav1.CreateOptions{})
72
		if err != nil {
73
			t.Fatalf("failed creating test service account: %v", err)
74
		}
75

76
		_, err = ki.CoreV1().Secrets(n).Create(context.TODO(), &v1.Secret{
77
			ObjectMeta: metav1.ObjectMeta{
78
				Name: "default-token-2",
79
				Annotations: map[string]string{
80
					"kubernetes.io/service-account.name": "default",
81
					"kubernetes.io/service-account.uid":  "1",
82
				},
83
			},
84
			Type: v1.SecretTypeServiceAccountToken,
85
			Data: map[string][]byte{
86
				"token": []byte("1"),
87
			},
88
		}, metav1.CreateOptions{})
89
		if err != nil {
90
			t.Fatalf("failed creating test secret: %v", err)
91
		}
92
	}
93
	fx.Clear()
94
}
95

96
func cleanup(ki kubernetes.Interface) {
97
	for _, n := range []string{"nsa", "nsb"} {
98
		n := n
99
		pods, err := ki.CoreV1().Pods(n).List(context.TODO(), metav1.ListOptions{})
100
		if err == nil {
101
			// Make sure the pods don't exist
102
			for _, pod := range pods.Items {
103
				_ = ki.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, metav1.DeleteOptions{})
104
			}
105
		}
106
	}
107
}
108

109
func TestPodCache(t *testing.T) {
110
	c, fx := NewFakeControllerWithOptions(t, FakeControllerOptions{
111
		WatchedNamespaces: "nsa,nsb",
112
	})
113

114
	initTestEnv(t, c.client.Kube(), fx)
115

116
	// Namespace must be lowercase (nsA doesn't work)
117
	pods := []*v1.Pod{
118
		generatePod("128.0.0.1", "cpod1", "nsa", "", "", map[string]string{"app": "test-app"}, map[string]string{}),
119
		generatePod("128.0.0.2", "cpod2", "nsa", "", "", map[string]string{"app": "prod-app-1"}, map[string]string{}),
120
		generatePod("128.0.0.3", "cpod3", "nsb", "", "", map[string]string{"app": "prod-app-2"}, map[string]string{}),
121
	}
122

123
	addPods(t, c, fx, pods...)
124

125
	// Verify podCache
126
	wantLabels := map[string]labels.Instance{
127
		"128.0.0.1": {"app": "test-app"},
128
		"128.0.0.2": {"app": "prod-app-1"},
129
		"128.0.0.3": {"app": "prod-app-2"},
130
	}
131
	for addr, wantTag := range wantLabels {
132
		pod := c.pods.getPodsByIP(addr)
133
		if pod == nil {
134
			t.Error("Not found ", addr)
135
			continue
136
		}
137
		if !reflect.DeepEqual(wantTag, labels.Instance(pod[0].Labels)) {
138
			t.Errorf("Expected %v got %v", wantTag, labels.Instance(pod[0].Labels))
139
		}
140
	}
141

142
	// This pod exists, but should not be in the cache because it is in a
143
	// namespace not watched by the controller.
144
	assert.Equal(t, c.pods.getPodsByIP("128.0.0.4"), nil)
145

146
	// This pod should not be in the cache because it never existed.
147
	assert.Equal(t, c.pods.getPodsByIP("128.0.0.128"), nil)
148
}
149

150
func TestHostNetworkPod(t *testing.T) {
151
	c, fx := NewFakeControllerWithOptions(t, FakeControllerOptions{})
152
	pods := clienttest.Wrap(t, c.podsClient)
153
	events := assert.NewTracker[string](t)
154
	c.AppendWorkloadHandler(func(instance *model.WorkloadInstance, event model.Event) {
155
		events.Record(fmt.Sprintf("%v/%v", instance.Name, event))
156
	})
157
	initTestEnv(t, c.client.Kube(), fx)
158
	createPod := func(ip, name string) {
159
		addPods(t, c, fx, generatePod(ip, name, "ns", "1", "", map[string]string{}, map[string]string{}))
160
	}
161

162
	createPod("128.0.0.1", "pod1")
163
	assert.Equal(t, c.pods.getPodKeys("128.0.0.1"), []types.NamespacedName{{Name: "pod1", Namespace: "ns"}})
164
	events.WaitOrdered("pod1/add", "pod1/update")
165
	createPod("128.0.0.1", "pod2")
166
	events.WaitOrdered("pod2/add", "pod2/update")
167
	assert.Equal(t, sets.New(c.pods.getPodKeys("128.0.0.1")...), sets.New(
168
		types.NamespacedName{Name: "pod1", Namespace: "ns"},
169
		types.NamespacedName{Name: "pod2", Namespace: "ns"},
170
	))
171

172
	p := c.pods.getPodByKey(types.NamespacedName{Name: "pod1", Namespace: "ns"})
173
	if p == nil || p.Name != "pod1" {
174
		t.Fatalf("unexpected pod: %v", p)
175
	}
176
	pods.Delete("pod1", "ns")
177
	pods.Delete("pod2", "ns")
178
	events.WaitOrdered("pod1/delete", "pod2/delete")
179
}
180

181
// Regression test for https://github.com/istio/istio/issues/20676
182
func TestIPReuse(t *testing.T) {
183
	c, fx := NewFakeControllerWithOptions(t, FakeControllerOptions{})
184
	pods := clienttest.Wrap(t, c.podsClient)
185
	initTestEnv(t, c.client.Kube(), fx)
186

187
	createPod := func(ip, name string) {
188
		addPods(t, c, fx, generatePod(ip, name, "ns", "1", "", map[string]string{}, map[string]string{}))
189
	}
190

191
	createPod("128.0.0.1", "pod")
192
	assert.Equal(t, c.pods.getPodKeys("128.0.0.1"), []types.NamespacedName{{Name: "pod", Namespace: "ns"}})
193

194
	// Change the pod IP. This can happen if the pod moves to another node, for example.
195
	createPod("128.0.0.2", "pod")
196
	assert.Equal(t, c.pods.getPodKeys("128.0.0.2"), []types.NamespacedName{{Name: "pod", Namespace: "ns"}})
197
	assert.Equal(t, c.pods.getPodKeys("128.0.0.1"), nil)
198

199
	// A new pod is created with the old IP. We should get new-pod, not pod
200
	createPod("128.0.0.1", "new-pod")
201
	assert.Equal(t, c.pods.getPodKeys("128.0.0.1"), []types.NamespacedName{{Name: "new-pod", Namespace: "ns"}})
202

203
	// A new pod is created with the same IP. This happens with hostNetwork, or maybe we miss an update somehow.
204
	createPod("128.0.0.1", "another-pod")
205
	assert.Equal(t, sets.New(c.pods.getPodKeys("128.0.0.1")...), sets.New(
206
		types.NamespacedName{Name: "new-pod", Namespace: "ns"},
207
		types.NamespacedName{Name: "another-pod", Namespace: "ns"},
208
	))
209

210
	fetch := func() sets.Set[types.NamespacedName] { return sets.New(c.pods.getPodKeys("128.0.0.1")...) }
211
	pods.Delete("another-pod", "ns")
212
	assert.EventuallyEqual(t, fetch, sets.New(types.NamespacedName{Name: "new-pod", Namespace: "ns"}))
213

214
	pods.Delete("new-pod", "ns")
215
	assert.EventuallyEqual(t, fetch, nil)
216
}
217

218
func waitForPod(t test.Failer, c *FakeController, ip string) {
219
	retry.UntilOrFail(t, func() bool {
220
		c.pods.RLock()
221
		defer c.pods.RUnlock()
222
		if _, ok := c.pods.podsByIP[ip]; ok {
223
			return true
224
		}
225
		return false
226
	})
227
}
228

229
func waitForNode(t test.Failer, c *FakeController, name string) {
230
	retry.UntilOrFail(t, func() bool {
231
		return c.nodes.Get(name, "") != nil
232
	}, retry.Timeout(time.Second*1), retry.Delay(time.Millisecond*5))
233
}
234

235
// Checks that events from the watcher create the proper internal structures
236
func TestPodCacheEvents(t *testing.T) {
237
	t.Parallel()
238
	c, _ := NewFakeControllerWithOptions(t, FakeControllerOptions{})
239

240
	ns := "default"
241
	podCache := c.pods
242

243
	handled := 0
244
	podCache.c.handlers.AppendWorkloadHandler(func(*model.WorkloadInstance, model.Event) {
245
		handled++
246
	})
247

248
	f := podCache.onEvent
249

250
	ip := "172.0.3.35"
251
	pod1 := metav1.ObjectMeta{Name: "pod1", Namespace: ns}
252
	if err := f(nil, &v1.Pod{ObjectMeta: pod1}, model.EventAdd); err != nil {
253
		t.Error(err)
254
	}
255

256
	notReadyCondition := []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionFalse}}
257
	readyCondition := []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue}}
258

259
	if err := f(nil,
260
		&v1.Pod{ObjectMeta: pod1, Status: v1.PodStatus{Conditions: notReadyCondition, PodIP: ip, Phase: v1.PodPending}},
261
		model.EventUpdate); err != nil {
262
		t.Error(err)
263
	}
264
	if handled != 0 {
265
		t.Errorf("notified workload handler %d times, want %d", handled, 0)
266
	}
267

268
	if err := f(nil, &v1.Pod{ObjectMeta: pod1, Status: v1.PodStatus{Conditions: readyCondition, PodIP: ip, Phase: v1.PodPending}}, model.EventUpdate); err != nil {
269
		t.Error(err)
270
	}
271
	if handled != 1 {
272
		t.Errorf("notified workload handler %d times, want %d", handled, 1)
273
	}
274
	assert.Equal(t, c.pods.getPodKeys(ip), []types.NamespacedName{{Name: "pod1", Namespace: "default"}})
275

276
	if err := f(nil,
277
		&v1.Pod{ObjectMeta: pod1, Status: v1.PodStatus{Conditions: readyCondition, PodIP: ip, Phase: v1.PodFailed}}, model.EventUpdate); err != nil {
278
		t.Error(err)
279
	}
280
	if handled != 2 {
281
		t.Errorf("notified workload handler %d times, want %d", handled, 2)
282
	}
283
	assert.Equal(t, podCache.getPodKeys(ip), nil)
284

285
	pod1.DeletionTimestamp = &metav1.Time{Time: time.Now()}
286
	if err := f(nil, &v1.Pod{ObjectMeta: pod1, Status: v1.PodStatus{PodIP: ip, Phase: v1.PodFailed}}, model.EventUpdate); err != nil {
287
		t.Error(err)
288
	}
289
	if handled != 2 {
290
		t.Errorf("notified workload handler %d times, want %d", handled, 2)
291
	}
292

293
	pod2 := metav1.ObjectMeta{Name: "pod2", Namespace: ns}
294
	if err := f(nil, &v1.Pod{ObjectMeta: pod2, Status: v1.PodStatus{Conditions: readyCondition, PodIP: ip, Phase: v1.PodRunning}}, model.EventAdd); err != nil {
295
		t.Error(err)
296
	}
297
	if handled != 3 {
298
		t.Errorf("notified workload handler %d times, want %d", handled, 3)
299
	}
300
	assert.Equal(t, sets.New(c.pods.getPodKeys(ip)...), sets.New(types.NamespacedName{Name: "pod2", Namespace: "default"}))
301

302
	if err := f(nil, &v1.Pod{ObjectMeta: pod1, Status: v1.PodStatus{PodIP: ip, Phase: v1.PodFailed}}, model.EventDelete); err != nil {
303
		t.Error(err)
304
	}
305
	if handled != 3 {
306
		t.Errorf("notified workload handler %d times, want %d", handled, 3)
307
	}
308
	assert.Equal(t, sets.New(c.pods.getPodKeys(ip)...), sets.New(types.NamespacedName{Name: "pod2", Namespace: "default"}))
309

310
	if err := f(nil, &v1.Pod{ObjectMeta: pod2, Spec: v1.PodSpec{
311
		RestartPolicy: v1.RestartPolicyOnFailure,
312
	}, Status: v1.PodStatus{Conditions: readyCondition, PodIP: ip, Phase: v1.PodFailed}}, model.EventUpdate); err != nil {
313
		t.Error(err)
314
	}
315
	if handled != 4 {
316
		t.Errorf("notified workload handler %d times, want %d", handled, 4)
317
	}
318

319
	assert.Equal(t, c.pods.getPodsByIP(ip), nil)
320

321
	if err := f(nil, &v1.Pod{ObjectMeta: pod2, Status: v1.PodStatus{Conditions: readyCondition, PodIP: ip, Phase: v1.PodFailed}}, model.EventDelete); err != nil {
322
		t.Error(err)
323
	}
324
	if handled != 4 {
325
		t.Errorf("notified workload handler %d times, want %d", handled, 5)
326
	}
327
}
328

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.