istio

Форк
0
342 строки · 10.7 Кб
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
	"fmt"
19
	"sync"
20
	"testing"
21
	"time"
22

23
	corev1 "k8s.io/api/core/v1"
24
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25
	"k8s.io/apimachinery/pkg/runtime/schema"
26
	k8sv1 "sigs.k8s.io/gateway-api/apis/v1"
27
	"sigs.k8s.io/gateway-api/apis/v1beta1"
28

29
	"istio.io/api/label"
30
	meshconfig "istio.io/api/mesh/v1alpha1"
31
	"istio.io/istio/pilot/pkg/features"
32
	"istio.io/istio/pilot/pkg/model"
33
	"istio.io/istio/pkg/config/constants"
34
	"istio.io/istio/pkg/config/mesh"
35
	"istio.io/istio/pkg/config/schema/gvr"
36
	"istio.io/istio/pkg/kube/controllers"
37
	"istio.io/istio/pkg/kube/kclient"
38
	"istio.io/istio/pkg/kube/kclient/clienttest"
39
	"istio.io/istio/pkg/test"
40
	"istio.io/istio/pkg/test/util/assert"
41
	"istio.io/istio/pkg/test/util/retry"
42
	"istio.io/istio/pkg/util/sets"
43
)
44

45
func TestNetworkUpdateTriggers(t *testing.T) {
46
	test.SetForTest(t, &features.MultiNetworkGatewayAPI, true)
47
	meshNetworks := mesh.NewFixedNetworksWatcher(nil)
48
	c, _ := NewFakeControllerWithOptions(t, FakeControllerOptions{
49
		ClusterID:       constants.DefaultClusterName,
50
		NetworksWatcher: meshNetworks,
51
		DomainSuffix:    "cluster.local",
52
		CRDs:            []schema.GroupVersionResource{gvr.KubernetesGateway},
53
	})
54

55
	if len(c.NetworkGateways()) != 0 {
56
		t.Fatal("did not expect any gateways yet")
57
	}
58

59
	notifyCh := make(chan struct{}, 1)
60
	var (
61
		gwMu sync.Mutex
62
		gws  []model.NetworkGateway
63
	)
64
	setGws := func(v []model.NetworkGateway) {
65
		gwMu.Lock()
66
		defer gwMu.Unlock()
67
		gws = v
68
	}
69
	getGws := func() []model.NetworkGateway {
70
		gwMu.Lock()
71
		defer gwMu.Unlock()
72
		return gws
73
	}
74

75
	c.AppendNetworkGatewayHandler(func() {
76
		setGws(c.NetworkGateways())
77
		notifyCh <- struct{}{}
78
	})
79
	expectGateways := func(t *testing.T, expectedGws int) {
80
		// wait for a notification
81
		assert.ChannelHasItem(t, notifyCh)
82
		if n := len(getGws()); n != expectedGws {
83
			t.Errorf("expected %d gateways but got %d", expectedGws, n)
84
		}
85
	}
86

87
	t.Run("add meshnetworks", func(t *testing.T) {
88
		addMeshNetworksFromRegistryGateway(t, c, meshNetworks)
89
		expectGateways(t, 2)
90
	})
91
	t.Run("add labeled service", func(t *testing.T) {
92
		addLabeledServiceGateway(t, c, "nw0")
93
		expectGateways(t, 3)
94
	})
95
	t.Run("update labeled service network", func(t *testing.T) {
96
		addLabeledServiceGateway(t, c, "nw1")
97
		expectGateways(t, 3)
98
	})
99
	t.Run("add kubernetes gateway", func(t *testing.T) {
100
		addOrUpdateGatewayResource(t, c, 35443)
101
		expectGateways(t, 7)
102
	})
103
	t.Run("update kubernetes gateway", func(t *testing.T) {
104
		addOrUpdateGatewayResource(t, c, 45443)
105
		expectGateways(t, 7)
106
	})
107
	t.Run("remove kubernetes gateway", func(t *testing.T) {
108
		removeGatewayResource(t, c)
109
		expectGateways(t, 3)
110
	})
111
	t.Run("remove labeled service", func(t *testing.T) {
112
		removeLabeledServiceGateway(t, c)
113
		expectGateways(t, 2)
114
	})
115
	// gateways are created even with out service
116
	t.Run("add kubernetes gateway", func(t *testing.T) {
117
		addOrUpdateGatewayResource(t, c, 35443)
118
		expectGateways(t, 6)
119
	})
120
	t.Run("remove kubernetes gateway", func(t *testing.T) {
121
		removeGatewayResource(t, c)
122
		expectGateways(t, 2)
123
	})
124
	t.Run("remove meshnetworks", func(t *testing.T) {
125
		meshNetworks.SetNetworks(nil)
126
		expectGateways(t, 0)
127
	})
128
}
129

130
func addLabeledServiceGateway(t *testing.T, c *FakeController, nw string) {
131
	svc := &corev1.Service{
132
		ObjectMeta: metav1.ObjectMeta{Name: "istio-labeled-gw", Namespace: "arbitrary-ns", Labels: map[string]string{
133
			label.TopologyNetwork.Name: nw,
134
		}},
135
		Spec: corev1.ServiceSpec{
136
			Type:  corev1.ServiceTypeLoadBalancer,
137
			Ports: []corev1.ServicePort{{Port: 15443, Protocol: corev1.ProtocolTCP}},
138
		},
139
		Status: corev1.ServiceStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{
140
			IP:    "2.3.4.6",
141
			Ports: []corev1.PortStatus{{Port: 15443, Protocol: corev1.ProtocolTCP}},
142
		}}}},
143
	}
144
	clienttest.Wrap(t, c.services).CreateOrUpdate(svc)
145
}
146

147
func removeLabeledServiceGateway(t *testing.T, c *FakeController) {
148
	clienttest.Wrap(t, c.services).Delete("istio-labeled-gw", "arbitrary-ns")
149
}
150

151
// creates a gateway that exposes 2 ports that are valid auto-passthrough ports
152
// and it does so on an IP and a hostname
153
func addOrUpdateGatewayResource(t *testing.T, c *FakeController, customPort int) {
154
	passthroughMode := k8sv1.TLSModePassthrough
155
	ipType := v1beta1.IPAddressType
156
	hostnameType := v1beta1.HostnameAddressType
157
	clienttest.Wrap(t, kclient.New[*v1beta1.Gateway](c.client)).CreateOrUpdate(&v1beta1.Gateway{
158
		ObjectMeta: metav1.ObjectMeta{
159
			Name:      "eastwest-gwapi",
160
			Namespace: "istio-system",
161
			Labels:    map[string]string{label.TopologyNetwork.Name: "nw2"},
162
		},
163
		Spec: v1beta1.GatewaySpec{
164
			GatewayClassName: "istio",
165
			Addresses: []v1beta1.GatewayAddress{
166
				{Type: &ipType, Value: "1.2.3.4"},
167
				{Type: &hostnameType, Value: "some hostname"},
168
			},
169
			Listeners: []v1beta1.Listener{
170
				{
171
					Name: "detected-by-options",
172
					TLS: &v1beta1.GatewayTLSConfig{
173
						Mode: &passthroughMode,
174
						Options: map[v1beta1.AnnotationKey]v1beta1.AnnotationValue{
175
							constants.ListenerModeOption: constants.ListenerModeAutoPassthrough,
176
						},
177
					},
178
					Port: v1beta1.PortNumber(customPort),
179
				},
180
				{
181
					Name: "detected-by-number",
182
					TLS:  &v1beta1.GatewayTLSConfig{Mode: &passthroughMode},
183
					Port: 15443,
184
				},
185
			},
186
		},
187
		Status: v1beta1.GatewayStatus{},
188
	})
189
}
190

191
func removeGatewayResource(t *testing.T, c *FakeController) {
192
	clienttest.Wrap(t, kclient.New[*v1beta1.Gateway](c.client)).Delete("eastwest-gwapi", "istio-system")
193
}
194

195
func addMeshNetworksFromRegistryGateway(t *testing.T, c *FakeController, watcher mesh.NetworksWatcher) {
196
	clienttest.Wrap(t, c.services).Create(&corev1.Service{
197
		ObjectMeta: metav1.ObjectMeta{Name: "istio-meshnetworks-gw", Namespace: "istio-system"},
198
		Spec: corev1.ServiceSpec{
199
			Type:  corev1.ServiceTypeLoadBalancer,
200
			Ports: []corev1.ServicePort{{Port: 15443, Protocol: corev1.ProtocolTCP}},
201
		},
202
		Status: corev1.ServiceStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{
203
			IP:    "1.2.3.4",
204
			Ports: []corev1.PortStatus{{Port: 15443, Protocol: corev1.ProtocolTCP}},
205
		}}}},
206
	})
207
	watcher.SetNetworks(&meshconfig.MeshNetworks{Networks: map[string]*meshconfig.Network{
208
		"nw0": {
209
			Endpoints: []*meshconfig.Network_NetworkEndpoints{{
210
				Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{FromRegistry: "Kubernetes"},
211
			}},
212
			Gateways: []*meshconfig.Network_IstioNetworkGateway{{
213
				Port: 15443,
214
				Gw:   &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{RegistryServiceName: "istio-meshnetworks-gw.istio-system.svc.cluster.local"},
215
			}},
216
		},
217
		"nw1": {
218
			Endpoints: []*meshconfig.Network_NetworkEndpoints{{
219
				Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{FromRegistry: "Kubernetes"},
220
			}},
221
			Gateways: []*meshconfig.Network_IstioNetworkGateway{{
222
				Port: 15443,
223
				Gw:   &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{RegistryServiceName: "istio-meshnetworks-gw.istio-system.svc.cluster.local"},
224
			}},
225
		},
226
	}})
227
}
228

229
func TestAmbientSystemNamespaceNetworkChange(t *testing.T) {
230
	test.SetForTest(t, &features.EnableAmbientControllers, true)
231
	testNS := "test"
232
	systemNS := "istio-system"
233

234
	s, fx := NewFakeControllerWithOptions(t, FakeControllerOptions{
235
		SystemNamespace: systemNS,
236
		NetworksWatcher: mesh.NewFixedNetworksWatcher(nil),
237
	})
238

239
	tracker := assert.NewTracker[string](t)
240

241
	s.namespaces.AddEventHandler(controllers.ObjectHandler(func(o controllers.Object) {
242
		tracker.Record(o.GetName())
243
	}))
244

245
	expectNetwork := func(t *testing.T, c *FakeController, network string) {
246
		t.Helper()
247
		retry.UntilSuccessOrFail(t, func() error {
248
			t.Helper()
249
			if c.networkFromSystemNamespace().String() != network {
250
				return fmt.Errorf("no network system notify")
251
			}
252
			podNames := sets.New[string]("pod1", "pod2")
253
			svcNames := sets.New[string]("svc1")
254
			addresses := c.ambientIndex.All()
255
			for _, addr := range addresses {
256
				wl := addr.GetWorkload()
257
				if wl != nil {
258
					if !podNames.Contains(wl.Name) {
259
						continue
260
					}
261
					if addr.GetWorkload().Network != network {
262
						return fmt.Errorf("no network workload notify")
263
					}
264
				}
265
				svc := addr.GetService()
266
				if svc != nil {
267
					if !svcNames.Contains(svc.Name) {
268
						continue
269
					}
270
					for _, saddr := range svc.GetAddresses() {
271
						if saddr.GetNetwork() != network {
272
							return fmt.Errorf("no network service notify")
273
						}
274
					}
275
				}
276
			}
277
			return nil
278
		}, retry.Timeout(time.Second*5))
279
	}
280

281
	pc := clienttest.NewWriter[*corev1.Pod](t, s.client)
282
	sc := clienttest.NewWriter[*corev1.Service](t, s.client)
283
	pod1 := generatePod("127.0.0.1", "pod1", testNS, "sa1", "node1", map[string]string{"app": "a"}, nil)
284
	pc.CreateOrUpdateStatus(pod1)
285
	fx.WaitOrFail(t, "xds")
286

287
	pod2 := generatePod("127.0.0.2", "pod2", testNS, "sa2", "node1", map[string]string{"app": "a"}, nil)
288
	pc.CreateOrUpdateStatus(pod2)
289
	fx.WaitOrFail(t, "xds")
290

291
	sc.CreateOrUpdate(generateService("svc1", testNS, map[string]string{}, // labels
292
		map[string]string{}, // annotations
293
		[]int32{80},
294
		map[string]string{"app": "a"}, // selector
295
		"10.0.0.1",
296
	))
297
	fx.WaitOrFail(t, "xds")
298

299
	createOrUpdateNamespace(t, s, testNS, "")
300
	createOrUpdateNamespace(t, s, systemNS, "")
301

302
	tracker.WaitOrdered(testNS, systemNS)
303

304
	t.Run("change namespace network to nw1", func(t *testing.T) {
305
		createOrUpdateNamespace(t, s, systemNS, "nw1")
306
		tracker.WaitOrdered(systemNS)
307
		expectNetwork(t, s, "nw1")
308
	})
309

310
	t.Run("change namespace network to nw2", func(t *testing.T) {
311
		createOrUpdateNamespace(t, s, systemNS, "nw2")
312
		tracker.WaitOrdered(systemNS)
313
		expectNetwork(t, s, "nw2")
314
	})
315

316
	t.Run("manually change namespace network to nw3, and update meshNetworks", func(t *testing.T) {
317
		s.setNetworkFromNamespace(&corev1.Namespace{
318
			ObjectMeta: metav1.ObjectMeta{
319
				Name: systemNS,
320
				Labels: map[string]string{
321
					label.TopologyNetwork.Name: "nw3",
322
				},
323
			},
324
		})
325
		createOrUpdateNamespace(t, s, systemNS, "nw3")
326
		tracker.WaitOrdered(systemNS)
327
		addMeshNetworksFromRegistryGateway(t, s, s.meshNetworksWatcher)
328
		expectNetwork(t, s, "nw3")
329
	})
330
}
331

332
func createOrUpdateNamespace(t *testing.T, c *FakeController, name, network string) {
333
	namespace := &corev1.Namespace{
334
		ObjectMeta: metav1.ObjectMeta{
335
			Name: name,
336
			Labels: map[string]string{
337
				label.TopologyNetwork.Name: network,
338
			},
339
		},
340
	}
341
	clienttest.Wrap(t, c.namespaces).CreateOrUpdate(namespace)
342
}
343

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

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

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

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