istio

Форк
0
2362 строки · 91.2 Кб
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 serviceentry
16

17
import (
18
	"fmt"
19
	"net"
20
	"reflect"
21
	"sort"
22
	"strings"
23
	"testing"
24
	"time"
25

26
	"istio.io/api/label"
27
	networking "istio.io/api/networking/v1alpha3"
28
	"istio.io/istio/pilot/pkg/config/memory"
29
	"istio.io/istio/pilot/pkg/model"
30
	"istio.io/istio/pilot/pkg/serviceregistry/util/xdsfake"
31
	"istio.io/istio/pkg/config"
32
	"istio.io/istio/pkg/config/constants"
33
	"istio.io/istio/pkg/config/host"
34
	"istio.io/istio/pkg/config/labels"
35
	"istio.io/istio/pkg/config/schema/collections"
36
	"istio.io/istio/pkg/config/schema/gvk"
37
	"istio.io/istio/pkg/maps"
38
	"istio.io/istio/pkg/ptr"
39
	"istio.io/istio/pkg/slices"
40
	"istio.io/istio/pkg/spiffe"
41
	"istio.io/istio/pkg/test"
42
	"istio.io/istio/pkg/test/util/assert"
43
	"istio.io/istio/pkg/test/util/retry"
44
)
45

46
func createConfigs(configs []*config.Config, store model.ConfigStore, t testing.TB) {
47
	t.Helper()
48
	for _, cfg := range configs {
49
		_, err := store.Create(*cfg)
50
		if err != nil && strings.Contains(err.Error(), "item already exists") {
51
			_, err := store.Update(*cfg)
52
			if err != nil {
53
				t.Fatalf("error occurred updating ServiceEntry config: %v", err)
54
			}
55
		} else if err != nil {
56
			t.Fatalf("error occurred creating ServiceEntry config: %v", err)
57
		}
58
	}
59
}
60

61
func callInstanceHandlers(instances []*model.WorkloadInstance, sd *Controller, ev model.Event, t testing.TB) {
62
	t.Helper()
63
	for _, instance := range instances {
64
		sd.WorkloadInstanceHandler(instance, ev)
65
	}
66
}
67

68
func deleteConfigs(configs []*config.Config, store model.ConfigStore, t testing.TB) {
69
	t.Helper()
70
	for _, cfg := range configs {
71
		err := store.Delete(cfg.GroupVersionKind, cfg.Name, cfg.Namespace, nil)
72
		if err != nil {
73
			t.Errorf("error occurred crearting ServiceEntry config: %v", err)
74
		}
75
	}
76
}
77

78
type Event = xdsfake.Event
79

80
func initServiceDiscovery(t test.Failer) (model.ConfigStore, *Controller, *xdsfake.Updater) {
81
	return initServiceDiscoveryWithOpts(t, false)
82
}
83

84
// initServiceDiscoveryWithoutEvents initializes a test setup with no events. This avoids excessive attempts to push
85
// EDS updates to a full queue
86
func initServiceDiscoveryWithoutEvents(t test.Failer) (model.ConfigStore, *Controller) {
87
	store := memory.Make(collections.Pilot)
88
	configController := memory.NewController(store)
89

90
	stop := test.NewStop(t)
91
	go configController.Run(stop)
92
	fx := xdsfake.NewFakeXDS()
93
	go func() {
94
		for {
95
			select {
96
			case <-stop:
97
				return
98
			case <-fx.Events: // drain
99
			}
100
		}
101
	}()
102

103
	serviceController := NewController(configController, fx)
104
	return configController, serviceController
105
}
106

107
func initServiceDiscoveryWithOpts(t test.Failer, workloadOnly bool, opts ...Option) (model.ConfigStore, *Controller, *xdsfake.Updater) {
108
	store := memory.Make(collections.Pilot)
109
	configController := memory.NewSyncController(store)
110

111
	stop := test.NewStop(t)
112
	go configController.Run(stop)
113

114
	endpoints := model.NewEndpointIndex(model.DisabledCache{})
115
	delegate := model.NewEndpointIndexUpdater(endpoints)
116
	xdsUpdater := xdsfake.NewWithDelegate(delegate)
117

118
	istioStore := configController
119
	var controller *Controller
120
	if !workloadOnly {
121
		controller = NewController(configController, xdsUpdater, opts...)
122
	} else {
123
		controller = NewWorkloadEntryController(configController, xdsUpdater, opts...)
124
	}
125
	go controller.Run(stop)
126
	return istioStore, controller, xdsUpdater
127
}
128

129
func TestServiceDiscoveryServices(t *testing.T) {
130
	store, sd, fx := initServiceDiscovery(t)
131
	expectedServices := []*model.Service{
132
		makeService("*.istio.io", "httpDNSRR", constants.UnspecifiedIP, map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSRoundRobinLB),
133
		makeService("*.google.com", "httpDNS", constants.UnspecifiedIP, map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSLB),
134
		makeService("tcpstatic.com", "tcpStatic", "172.217.0.1", map[string]int{"tcp-444": 444}, true, model.ClientSideLB),
135
	}
136

137
	createConfigs([]*config.Config{httpDNS, httpDNSRR, tcpStatic}, store, t)
138

139
	expectEvents(t, fx,
140
		Event{Type: "xds full", ID: "*.google.com"},
141
		Event{Type: "xds full", ID: "*.istio.io"},
142
		Event{Type: "xds full", ID: "tcpstatic.com"},
143
		Event{Type: "service", ID: "*.google.com", Namespace: httpDNS.Namespace},
144
		Event{Type: "eds cache", ID: "*.google.com", Namespace: httpDNS.Namespace},
145
		Event{Type: "service", ID: "*.istio.io", Namespace: httpDNSRR.Namespace},
146
		Event{Type: "eds cache", ID: "*.istio.io", Namespace: httpDNSRR.Namespace},
147
		Event{Type: "service", ID: "tcpstatic.com", Namespace: tcpStatic.Namespace},
148
		Event{Type: "eds cache", ID: "tcpstatic.com", Namespace: tcpStatic.Namespace})
149
	services := sd.Services()
150
	sortServices(services)
151
	sortServices(expectedServices)
152
	if err := compare(t, services, expectedServices); err != nil {
153
		t.Error(err)
154
	}
155
}
156

157
func TestServiceDiscoveryGetService(t *testing.T) {
158
	hostname := "*.google.com"
159
	hostDNE := "does.not.exist.local"
160

161
	store, sd, fx := initServiceDiscovery(t)
162

163
	createConfigs([]*config.Config{httpDNS, tcpStatic}, store, t)
164
	fx.WaitOrFail(t, "xds full")
165
	fx.WaitOrFail(t, "xds full")
166
	service := sd.GetService(host.Name(hostDNE))
167
	if service != nil {
168
		t.Errorf("GetService(%q) => should not exist, got %s", hostDNE, service.Hostname)
169
	}
170

171
	service = sd.GetService(host.Name(hostname))
172
	if service == nil {
173
		t.Fatalf("GetService(%q) => should exist", hostname)
174
	}
175
	if service.Hostname != host.Name(hostname) {
176
		t.Errorf("GetService(%q) => %q, want %q", hostname, service.Hostname, hostname)
177
	}
178
}
179

180
// TestServiceDiscoveryServiceUpdate test various add/update/delete events for ServiceEntry
181
// nolint: lll
182
func TestServiceDiscoveryServiceUpdate(t *testing.T) {
183
	store, sd, events := initServiceDiscovery(t)
184
	// httpStaticOverlayUpdated is the same as httpStaticOverlay but with an extra endpoint added to test updates
185
	httpStaticOverlayUpdated := func() *config.Config {
186
		c := httpStaticOverlay.DeepCopy()
187
		se := c.Spec.(*networking.ServiceEntry)
188
		se.Endpoints = append(se.Endpoints, &networking.WorkloadEntry{
189
			Address: "6.6.6.6",
190
			Labels:  map[string]string{"other": "bar"},
191
		})
192
		return &c
193
	}()
194
	// httpStaticOverlayUpdatedInstance is the same as httpStaticOverlayUpdated but with an extra endpoint added that has the same address
195
	httpStaticOverlayUpdatedInstance := func() *config.Config {
196
		c := httpStaticOverlayUpdated.DeepCopy()
197
		se := c.Spec.(*networking.ServiceEntry)
198
		se.Endpoints = append(se.Endpoints, &networking.WorkloadEntry{
199
			Address: "6.6.6.6",
200
			Labels:  map[string]string{"some-new-label": "bar"},
201
		})
202
		return &c
203
	}()
204

205
	// httpStaticOverlayUpdatedNop is the same as httpStaticOverlayUpdated but with a NOP change
206
	httpStaticOverlayUpdatedNop := func() *config.Config {
207
		return ptr.Of(httpStaticOverlayUpdated.DeepCopy())
208
	}()
209

210
	// httpStaticOverlayUpdatedNs is the same as httpStaticOverlay but with an extra endpoint and different namespace added to test updates
211
	httpStaticOverlayUpdatedNs := func() *config.Config {
212
		c := httpStaticOverlay.DeepCopy()
213
		c.Namespace = "other"
214
		se := c.Spec.(*networking.ServiceEntry)
215
		se.Endpoints = append(se.Endpoints, &networking.WorkloadEntry{
216
			Address: "7.7.7.7",
217
			Labels:  map[string]string{"namespace": "bar"},
218
		})
219
		return &c
220
	}()
221

222
	// Setup the expected instances for `httpStatic`. This will be added/removed from as we add various configs
223
	baseInstances := []*model.ServiceInstance{
224
		makeInstance(httpStatic, "2.2.2.2", 7080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
225
		makeInstance(httpStatic, "2.2.2.2", 18080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS),
226
		makeInstance(httpStatic, "3.3.3.3", 1080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
227
		makeInstance(httpStatic, "3.3.3.3", 8080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS),
228
		makeInstance(httpStatic, "4.4.4.4", 1080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, PlainText),
229
		makeInstance(httpStatic, "4.4.4.4", 8080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"foo": "bar"}, PlainText),
230
	}
231

232
	t.Run("simple entry", func(t *testing.T) {
233
		// Create a SE, expect the base instances
234
		createConfigs([]*config.Config{httpStatic}, store, t)
235
		instances := baseInstances
236
		expectServiceInstances(t, sd, httpStatic, 0, instances)
237
		expectEvents(t, events,
238
			Event{Type: "service", ID: "*.google.com", Namespace: httpStatic.Namespace},
239
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStatic.Namespace},
240
			Event{Type: "xds full", ID: httpStatic.Spec.(*networking.ServiceEntry).Hosts[0]})
241
	})
242

243
	t.Run("add entry", func(t *testing.T) {
244
		// Create another SE for the same host, expect these instances to get added
245
		createConfigs([]*config.Config{httpStaticOverlay}, store, t)
246
		instances := append(baseInstances,
247
			makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText))
248
		expectServiceInstances(t, sd, httpStatic, 0, instances)
249
		expectEvents(t, events,
250
			Event{Type: "service", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace},
251
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace},
252
			Event{Type: "xds full", ID: httpStatic.Spec.(*networking.ServiceEntry).Hosts[0]})
253
	})
254

255
	t.Run("add endpoint", func(t *testing.T) {
256
		// Update the SE for the same host, expect these instances to get added
257
		createConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t)
258
		instances := append(baseInstances,
259
			makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
260
			makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText))
261
		expectServiceInstances(t, sd, httpStatic, 0, instances)
262
		expectEvents(t, events, Event{Type: "eds", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace, EndpointCount: len(instances)})
263

264
		// Make a NOP change, expect that there are no changes
265
		createConfigs([]*config.Config{httpStaticOverlayUpdatedNop}, store, t)
266
		expectServiceInstances(t, sd, httpStaticOverlayUpdatedNop, 0, instances)
267
		// TODO this could trigger no changes
268
		expectEvents(t, events, Event{Type: "eds", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace, EndpointCount: len(instances)})
269
	})
270

271
	t.Run("overlapping address", func(t *testing.T) {
272
		// Add another SE with an additional endpoint with a matching address
273
		createConfigs([]*config.Config{httpStaticOverlayUpdatedInstance}, store, t)
274
		instances := append(baseInstances,
275
			makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
276
			makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText),
277
			makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"some-new-label": "bar"}, PlainText))
278
		expectServiceInstances(t, sd, httpStaticOverlayUpdatedInstance, 0, instances)
279
		proxyInstances := []model.ServiceTarget{
280
			makeTarget(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText),
281
			makeTarget(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"some-new-label": "bar"}, PlainText),
282
		}
283
		expectProxyTargets(t, sd, proxyInstances, "6.6.6.6")
284
		// TODO 45 is wrong
285
		expectEvents(t, events, Event{Type: "eds", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace, EndpointCount: len(instances)})
286

287
		// Remove the additional endpoint
288
		createConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t)
289
		instances = append(baseInstances,
290
			makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
291
			makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText))
292
		expectServiceInstances(t, sd, httpStatic, 0, instances)
293
		proxyInstances = []model.ServiceTarget{
294
			makeTarget(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText),
295
		}
296
		expectProxyTargets(t, sd, proxyInstances, "6.6.6.6")
297
		expectEvents(t, events, Event{Type: "eds", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace, EndpointCount: len(instances)})
298
	})
299

300
	t.Run("update removes endpoint", func(t *testing.T) {
301
		// Update the SE for the same host to remove the endpoint
302
		createConfigs([]*config.Config{httpStaticOverlay}, store, t)
303
		instances := append(baseInstances,
304
			makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText))
305
		expectServiceInstances(t, sd, httpStaticOverlay, 0, instances)
306
		expectEvents(t, events,
307
			Event{Type: "eds", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace, EndpointCount: len(instances)})
308
	})
309

310
	t.Run("different namespace", func(t *testing.T) {
311
		// Update the SE for the same host in a different ns, expect these instances to get added
312
		createConfigs([]*config.Config{httpStaticOverlayUpdatedNs}, store, t)
313
		instances := []*model.ServiceInstance{
314
			makeInstance(httpStaticOverlayUpdatedNs, "5.5.5.5", 4567, httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
315
			makeInstance(httpStaticOverlayUpdatedNs, "7.7.7.7", 4567, httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"namespace": "bar"}, PlainText),
316
		}
317
		// This lookup is per-namespace, so we should only see the objects in the same namespace
318
		expectServiceInstances(t, sd, httpStaticOverlayUpdatedNs, 0, instances)
319
		// Expect a full push, as the Service has changed
320
		expectEvents(t, events,
321
			Event{Type: "service", ID: "*.google.com", Namespace: "other"},
322
			Event{Type: "eds cache", ID: "*.google.com", Namespace: "other"},
323
			Event{Type: "xds full", ID: httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Hosts[0]})
324
	})
325

326
	t.Run("delete entry", func(t *testing.T) {
327
		// Delete the additional SE in same namespace , expect it to get removed
328
		deleteConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t)
329
		expectServiceInstances(t, sd, httpStatic, 0, baseInstances)
330
		// Check the other namespace is untouched
331
		instances := []*model.ServiceInstance{
332
			makeInstance(httpStaticOverlayUpdatedNs, "5.5.5.5", 4567, httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
333
			makeInstance(httpStaticOverlayUpdatedNs, "7.7.7.7", 4567, httpStaticOverlayUpdatedNs.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"namespace": "bar"}, PlainText),
334
		}
335
		expectServiceInstances(t, sd, httpStaticOverlayUpdatedNs, 0, instances)
336
		// svcUpdate is not triggered since `httpStatic` is there and has instances, so we should
337
		// not delete the endpoints shards of "*.google.com". We xpect a full push as the service has changed.
338
		expectEvents(t, events,
339
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStaticOverlayUpdated.Namespace},
340
			Event{Type: "xds full", ID: "*.google.com"},
341
		)
342

343
		// delete httpStatic, no "*.google.com" service exists now.
344
		deleteConfigs([]*config.Config{httpStatic}, store, t)
345
		// svcUpdate is triggered since "*.google.com" in same namespace is deleted and
346
		// we need to delete endpoint shards. We expect a full push as the service has changed.
347
		expectEvents(t, events,
348
			Event{Type: "service", ID: "*.google.com", Namespace: httpStatic.Namespace},
349
			Event{Type: "xds full", ID: "*.google.com"},
350
		)
351

352
		// add back httpStatic
353
		createConfigs([]*config.Config{httpStatic}, store, t)
354
		instances = baseInstances
355
		expectServiceInstances(t, sd, httpStatic, 0, instances)
356
		expectEvents(t, events,
357
			Event{Type: "service", ID: "*.google.com", Namespace: httpStatic.Namespace},
358
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStatic.Namespace},
359
			Event{Type: "xds full", ID: httpStatic.Spec.(*networking.ServiceEntry).Hosts[0]})
360

361
		// Add back the ServiceEntry, expect these instances to get added
362
		createConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t)
363
		instances = append(baseInstances,
364
			makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
365
			makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText))
366
		expectServiceInstances(t, sd, httpStatic, 0, instances)
367
		// Service change, so we need a full push
368
		expectEvents(t, events,
369
			Event{Type: "service", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace},
370
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace},
371
			Event{Type: "xds full", ID: "*.google.com"})
372
	})
373

374
	t.Run("change target port", func(t *testing.T) {
375
		// Change the target port
376
		targetPortChanged := func() *config.Config {
377
			c := httpStaticOverlayUpdated.DeepCopy()
378
			c.Spec.(*networking.ServiceEntry).Ports[0].TargetPort = 33333
379
			return &c
380
		}()
381
		createConfigs([]*config.Config{targetPortChanged}, store, t)
382

383
		// Endpoint ports should be changed
384
		instances := append(baseInstances,
385
			makeInstance(httpStaticOverlay, "5.5.5.5", 33333, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
386
			makeInstance(httpStaticOverlay, "6.6.6.6", 33333, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText))
387
		expectServiceInstances(t, sd, targetPortChanged, 0, instances)
388

389
		// Expect a full push, as the target port has changed
390
		expectEvents(t, events,
391
			Event{Type: "service", ID: "*.google.com", Namespace: httpStaticOverlayUpdated.Namespace},
392
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStaticOverlayUpdated.Namespace},
393
			Event{Type: "xds full", ID: "*.google.com"})
394

395
		// Restore the target port
396
		createConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t)
397

398
		// Endpoint ports should be changed
399
		instances = append(baseInstances,
400
			makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
401
			makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText))
402
		expectServiceInstances(t, sd, targetPortChanged, 0, instances)
403
		// Expect a full push, as the target port has changed
404
		expectEvents(t, events,
405
			Event{Type: "service", ID: "*.google.com", Namespace: httpStaticOverlayUpdated.Namespace},
406
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStaticOverlayUpdated.Namespace},
407
			Event{Type: "xds full", ID: "*.google.com"})
408
	})
409

410
	t.Run("change host", func(t *testing.T) {
411
		// same as httpStaticOverlayUpdated but with an additional host
412
		httpStaticHost := func() *config.Config {
413
			c := httpStaticOverlayUpdated.DeepCopy()
414
			se := c.Spec.(*networking.ServiceEntry)
415
			se.Hosts = append(se.Hosts, "other.com")
416
			return &c
417
		}()
418
		createConfigs([]*config.Config{httpStaticHost}, store, t)
419
		instances := append(baseInstances,
420
			makeInstance(httpStaticOverlay, "5.5.5.5", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
421
			makeInstance(httpStaticOverlay, "6.6.6.6", 4567, httpStaticOverlay.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText))
422
		// This is not applied, just to make makeInstance pick the right service.
423
		otherHost := func() *config.Config {
424
			c := httpStaticOverlayUpdated.DeepCopy()
425
			se := c.Spec.(*networking.ServiceEntry)
426
			se.Hosts = []string{"other.com"}
427
			return &c
428
		}()
429
		instances2 := []*model.ServiceInstance{
430
			makeInstance(otherHost, "5.5.5.5", 4567, httpStaticHost.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"overlay": "bar"}, PlainText),
431
			makeInstance(otherHost, "6.6.6.6", 4567, httpStaticHost.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"other": "bar"}, PlainText),
432
		}
433
		expectServiceInstances(t, sd, httpStaticHost, 0, instances, instances2)
434
		// Service change, so we need a full push
435
		expectEvents(t, events,
436
			Event{Type: "service", ID: "other.com", Namespace: httpStaticOverlayUpdated.Namespace},
437
			Event{Type: "eds cache", ID: "other.com", Namespace: httpStaticOverlayUpdated.Namespace},
438
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStaticOverlayUpdated.Namespace},
439
			Event{Type: "xds full", ID: "other.com"}) // service added
440

441
		// restore this config and remove the added host.
442
		createConfigs([]*config.Config{httpStaticOverlayUpdated}, store, t)
443
		expectEvents(t, events,
444
			Event{Type: "service", ID: "other.com", Namespace: httpStatic.Namespace},
445
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStatic.Namespace},
446
			Event{Type: "xds full", ID: "other.com"}) // service deleted
447
	})
448

449
	t.Run("change dns endpoints", func(t *testing.T) {
450
		// Setup the expected instances for DNS. This will be added/removed from as we add various configs
451
		instances1 := []*model.ServiceInstance{
452
			makeInstance(tcpDNS, "lon.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0],
453
				nil, MTLS),
454
			makeInstance(tcpDNS, "in.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0],
455
				nil, MTLS),
456
		}
457

458
		// This is not applied, just to make makeInstance pick the right service.
459
		tcpDNSUpdated := func() *config.Config {
460
			c := tcpDNS.DeepCopy()
461
			se := c.Spec.(*networking.ServiceEntry)
462
			se.Endpoints = []*networking.WorkloadEntry{
463
				{
464
					Address: "lon.google.com",
465
					Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
466
				},
467
			}
468
			return &c
469
		}()
470

471
		instances2 := []*model.ServiceInstance{
472
			makeInstance(tcpDNS, "lon.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0],
473
				nil, MTLS),
474
		}
475

476
		createConfigs([]*config.Config{tcpDNS}, store, t)
477
		expectServiceInstances(t, sd, tcpDNS, 0, instances1)
478
		// Service change, so we need a full push
479
		expectEvents(t, events,
480
			Event{Type: "service", ID: "tcpdns.com", Namespace: tcpDNS.Namespace},
481
			Event{Type: "eds cache", ID: "tcpdns.com", Namespace: tcpDNS.Namespace},
482
			Event{Type: "xds full", ID: "tcpdns.com"}) // service added
483

484
		// now update the config
485
		createConfigs([]*config.Config{tcpDNSUpdated}, store, t)
486
		expectEvents(t, events,
487
			Event{Type: "xds full", ID: "tcpdns.com"},
488
			Event{Type: "eds cache", ID: "tcpdns.com"},
489
		) // service deleted
490
		expectServiceInstances(t, sd, tcpDNS, 0, instances2)
491
	})
492

493
	t.Run("change workload selector", func(t *testing.T) {
494
		// same as selector but with an additional host
495
		selector1 := func() *config.Config {
496
			c := httpStaticOverlay.DeepCopy()
497
			se := c.Spec.(*networking.ServiceEntry)
498
			se.Hosts = append(se.Hosts, "selector1.com")
499
			se.Endpoints = nil
500
			se.WorkloadSelector = &networking.WorkloadSelector{
501
				Labels: map[string]string{"app": "wle"},
502
			}
503
			return &c
504
		}()
505
		createConfigs([]*config.Config{selector1}, store, t)
506
		// Service change, so we need a full push
507
		expectEvents(t, events,
508
			Event{Type: "service", ID: "selector1.com", Namespace: httpStaticOverlay.Namespace},
509
			Event{Type: "service", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace},
510
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace},
511
			Event{Type: "xds full", ID: "*.google.com,selector1.com"}) // service added
512

513
		selector1Updated := func() *config.Config {
514
			c := selector1.DeepCopy()
515
			se := c.Spec.(*networking.ServiceEntry)
516
			se.WorkloadSelector = &networking.WorkloadSelector{
517
				Labels: map[string]string{"app": "wle1"},
518
			}
519
			return &c
520
		}()
521
		createConfigs([]*config.Config{selector1Updated}, store, t)
522
		expectEvents(t, events,
523
			Event{Type: "service", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace},
524
			Event{Type: "service", ID: "selector1.com", Namespace: httpStaticOverlay.Namespace},
525
			Event{Type: "eds cache", ID: "*.google.com", Namespace: httpStaticOverlay.Namespace},
526
			Event{Type: "xds full", ID: "*.google.com,selector1.com"}) // service updated
527
	})
528
}
529

530
func TestServiceDiscoveryWorkloadUpdate(t *testing.T) {
531
	store, sd, events := initServiceDiscovery(t)
532

533
	// Setup a couple workload entries for test. These will be selected by the `selector` SE
534
	wle := createWorkloadEntry("wl", selector.Name,
535
		&networking.WorkloadEntry{
536
			Address:        "2.2.2.2",
537
			Labels:         map[string]string{"app": "wle"},
538
			ServiceAccount: "default",
539
		})
540
	wle2 := createWorkloadEntry("wl2", selector.Name,
541
		&networking.WorkloadEntry{
542
			Address:        "3.3.3.3",
543
			Labels:         map[string]string{"app": "wle"},
544
			ServiceAccount: "default",
545
		})
546
	wle3 := createWorkloadEntry("wl3", selector.Name,
547
		&networking.WorkloadEntry{
548
			Address:        "abc.def",
549
			Labels:         map[string]string{"app": "wle"},
550
			ServiceAccount: "default",
551
		})
552
	dnsWle := createWorkloadEntry("dnswl", dnsSelector.Namespace,
553
		&networking.WorkloadEntry{
554
			Address:        "4.4.4.4",
555
			Labels:         map[string]string{"app": "dns-wle"},
556
			ServiceAccount: "default",
557
		})
558

559
	t.Run("service entry", func(t *testing.T) {
560
		// Add just the ServiceEntry with selector. We should see no instances
561
		createConfigs([]*config.Config{selector}, store, t)
562
		instances := []*model.ServiceInstance{}
563
		expectProxyInstances(t, sd, instances, "2.2.2.2")
564
		expectServiceInstances(t, sd, selector, 0, instances)
565
		expectEvents(t, events,
566
			Event{Type: "service", ID: "selector.com", Namespace: selector.Namespace},
567
			Event{Type: "eds cache", ID: "selector.com", Namespace: selector.Namespace},
568
			Event{Type: "xds full", ID: "selector.com"})
569
	})
570

571
	t.Run("add workload", func(t *testing.T) {
572
		// Add a WLE, we expect this to update
573
		createConfigs([]*config.Config{wle}, store, t)
574

575
		instances := []*model.ServiceInstance{
576
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
577
				selector.Spec.(*networking.ServiceEntry).Ports[0],
578
				map[string]string{"app": "wle"}, "default"),
579
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
580
				selector.Spec.(*networking.ServiceEntry).Ports[1],
581
				map[string]string{"app": "wle"}, "default"),
582
		}
583
		for _, i := range instances {
584
			i.Endpoint.WorkloadName = "wl"
585
			i.Endpoint.Namespace = selector.Name
586
		}
587
		expectProxyInstances(t, sd, instances, "2.2.2.2")
588
		expectServiceInstances(t, sd, selector, 0, instances)
589
		expectEvents(t, events,
590
			Event{Type: "proxy", ID: "2.2.2.2"},
591
			Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 2},
592
		)
593
	})
594

595
	t.Run("update service entry host", func(t *testing.T) {
596
		updated := func() *config.Config {
597
			d := selector.DeepCopy()
598
			se := d.Spec.(*networking.ServiceEntry)
599
			se.Hosts = []string{"updated.com"}
600
			return &d
601
		}()
602

603
		instances := []*model.ServiceInstance{
604
			makeInstanceWithServiceAccount(updated, "2.2.2.2", 444,
605
				updated.Spec.(*networking.ServiceEntry).Ports[0],
606
				map[string]string{"app": "wle"}, "default"),
607
			makeInstanceWithServiceAccount(updated, "2.2.2.2", 445,
608
				updated.Spec.(*networking.ServiceEntry).Ports[1],
609
				map[string]string{"app": "wle"}, "default"),
610
		}
611
		for _, i := range instances {
612
			i.Endpoint.WorkloadName = "wl"
613
			i.Endpoint.Namespace = updated.Name
614
		}
615

616
		createConfigs([]*config.Config{updated}, store, t)
617
		expectProxyInstances(t, sd, instances, "2.2.2.2")
618
		expectServiceInstances(t, sd, selector, 0, []*model.ServiceInstance{})
619
		expectServiceInstances(t, sd, updated, 0, instances)
620
		expectEvents(t, events,
621
			Event{Type: "service", ID: "updated.com", Namespace: selector.Namespace},
622
			Event{Type: "service", ID: "selector.com", Namespace: selector.Namespace},
623
			Event{Type: "eds cache", ID: "updated.com", Namespace: selector.Namespace},
624
			Event{Type: "xds full", ID: "selector.com,updated.com"},
625
		)
626
	})
627

628
	t.Run("restore service entry host", func(t *testing.T) {
629
		instances := []*model.ServiceInstance{
630
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
631
				selector.Spec.(*networking.ServiceEntry).Ports[0],
632
				map[string]string{"app": "wle"}, "default"),
633
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
634
				selector.Spec.(*networking.ServiceEntry).Ports[1],
635
				map[string]string{"app": "wle"}, "default"),
636
		}
637
		for _, i := range instances {
638
			i.Endpoint.WorkloadName = "wl"
639
			i.Endpoint.Namespace = selector.Name
640
		}
641
		updated := func() *config.Config {
642
			d := selector.DeepCopy()
643
			se := d.Spec.(*networking.ServiceEntry)
644
			se.Hosts = []string{"updated.com"}
645
			return &d
646
		}()
647

648
		createConfigs([]*config.Config{selector}, store, t)
649
		expectProxyInstances(t, sd, instances, "2.2.2.2")
650
		expectServiceInstances(t, sd, selector, 0, instances)
651
		expectServiceInstances(t, sd, updated, 0, []*model.ServiceInstance{})
652
		expectEvents(t, events,
653
			Event{Type: "service", ID: "selector.com", Namespace: selector.Namespace},
654
			Event{Type: "service", ID: "updated.com", Namespace: selector.Namespace},
655
			Event{Type: "eds cache", ID: "selector.com", Namespace: selector.Namespace},
656
			Event{Type: "xds full", ID: "selector.com,updated.com"},
657
		)
658
	})
659

660
	t.Run("add dns service entry", func(t *testing.T) {
661
		// Add just the ServiceEntry with selector. We should see no instances
662
		createConfigs([]*config.Config{dnsSelector}, store, t)
663
		instances := []*model.ServiceInstance{}
664
		expectProxyInstances(t, sd, instances, "4.4.4.4")
665
		expectServiceInstances(t, sd, dnsSelector, 0, instances)
666
		expectEvents(t, events,
667
			Event{Type: "service", ID: "dns.selector.com", Namespace: dnsSelector.Namespace},
668
			Event{Type: "eds cache", ID: "dns.selector.com", Namespace: dnsSelector.Namespace},
669
			Event{Type: "xds full", ID: "dns.selector.com"})
670
	})
671

672
	t.Run("add dns workload", func(t *testing.T) {
673
		// Add a WLE, we expect this to update
674
		createConfigs([]*config.Config{dnsWle}, store, t)
675
		instances := []*model.ServiceInstance{
676
			makeInstanceWithServiceAccount(dnsSelector, "4.4.4.4", 444,
677
				selector.Spec.(*networking.ServiceEntry).Ports[0],
678
				map[string]string{"app": "dns-wle"}, "default"),
679
			makeInstanceWithServiceAccount(dnsSelector, "4.4.4.4", 445,
680
				selector.Spec.(*networking.ServiceEntry).Ports[1],
681
				map[string]string{"app": "dns-wle"}, "default"),
682
		}
683
		for _, i := range instances {
684
			i.Endpoint.WorkloadName = "dnswl"
685
			i.Endpoint.Namespace = dnsSelector.Namespace
686
		}
687
		expectProxyInstances(t, sd, instances, "4.4.4.4")
688
		expectServiceInstances(t, sd, dnsSelector, 0, instances)
689
		expectEvents(t, events,
690
			Event{Type: "eds cache", ID: "dns.selector.com", Namespace: dnsSelector.Namespace},
691
			Event{Type: "xds full", ID: "dns.selector.com"})
692
	})
693

694
	t.Run("another workload", func(t *testing.T) {
695
		// Add a different WLE
696
		createConfigs([]*config.Config{wle2}, store, t)
697
		instances := []*model.ServiceInstance{
698
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
699
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
700
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
701
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
702
		}
703
		for _, i := range instances {
704
			i.Endpoint.WorkloadName = "wl"
705
			i.Endpoint.Namespace = selector.Name
706
		}
707
		expectProxyInstances(t, sd, instances, "2.2.2.2")
708
		instances = append(instances,
709
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 444,
710
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
711
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 445,
712
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"))
713
		for _, i := range instances[2:] {
714
			i.Endpoint.WorkloadName = "wl2"
715
			i.Endpoint.Namespace = selector.Name
716
		}
717
		expectServiceInstances(t, sd, selector, 0, instances)
718
		expectEvents(t, events,
719
			Event{Type: "proxy", ID: "3.3.3.3"},
720
			Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 4},
721
		)
722
	})
723

724
	t.Run("ignore host workload", func(t *testing.T) {
725
		// Add a WLE with host address. Should be ignored by static service entry.
726
		createConfigs([]*config.Config{wle3}, store, t)
727
		instances := []*model.ServiceInstance{
728
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
729
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
730
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
731
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
732
		}
733
		for _, i := range instances {
734
			i.Endpoint.WorkloadName = "wl"
735
			i.Endpoint.Namespace = selector.Name
736
		}
737
		expectProxyInstances(t, sd, instances, "2.2.2.2")
738
		instances = append(instances,
739
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 444,
740
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
741
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 445,
742
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"))
743
		for _, i := range instances[2:] {
744
			i.Endpoint.WorkloadName = "wl2"
745
			i.Endpoint.Namespace = selector.Name
746
		}
747
		expectServiceInstances(t, sd, selector, 0, instances)
748
		expectEvents(t, events,
749
			Event{Type: "proxy", ID: "abc.def"},
750
		)
751
	})
752

753
	t.Run("deletion", func(t *testing.T) {
754
		// Delete the configs, it should be gone
755
		deleteConfigs([]*config.Config{wle2}, store, t)
756
		instances := []*model.ServiceInstance{
757
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
758
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
759
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
760
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
761
		}
762
		for _, i := range instances {
763
			i.Endpoint.WorkloadName = "wl"
764
			i.Endpoint.Namespace = selector.Name
765
		}
766
		expectProxyInstances(t, sd, instances, "2.2.2.2")
767
		expectServiceInstances(t, sd, selector, 0, instances)
768
		expectEvents(t, events, Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 2})
769

770
		// Delete the other config
771
		deleteConfigs([]*config.Config{wle}, store, t)
772
		instances = []*model.ServiceInstance{}
773
		expectServiceInstances(t, sd, selector, 0, instances)
774
		expectProxyInstances(t, sd, instances, "2.2.2.2")
775
		expectEvents(t, events, Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 0})
776

777
		// Add the config back
778
		createConfigs([]*config.Config{wle}, store, t)
779
		instances = []*model.ServiceInstance{
780
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
781
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
782
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
783
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
784
		}
785
		for _, i := range instances {
786
			i.Endpoint.WorkloadName = "wl"
787
			i.Endpoint.Namespace = selector.Name
788
		}
789
		expectProxyInstances(t, sd, instances, "2.2.2.2")
790
		expectServiceInstances(t, sd, selector, 0, instances)
791
		expectEvents(t, events,
792
			Event{Type: "proxy", ID: "2.2.2.2"},
793
			Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 2},
794
		)
795
	})
796

797
	t.Run("update", func(t *testing.T) {
798
		updated := func() *config.Config {
799
			d := wle.DeepCopy()
800
			we := d.Spec.(*networking.WorkloadEntry)
801
			we.Address = "9.9.9.9"
802
			return &d
803
		}()
804
		// Update the configs
805
		createConfigs([]*config.Config{updated}, store, t)
806
		instances := []*model.ServiceInstance{
807
			makeInstanceWithServiceAccount(selector, "9.9.9.9", 444,
808
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
809
			makeInstanceWithServiceAccount(selector, "9.9.9.9", 445,
810
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
811
		}
812
		for _, i := range instances {
813
			i.Endpoint.WorkloadName = "wl"
814
			i.Endpoint.Namespace = selector.Name
815
		}
816
		// Old IP is gone
817
		expectProxyInstances(t, sd, nil, "2.2.2.2")
818
		expectProxyInstances(t, sd, instances, "9.9.9.9")
819
		expectServiceInstances(t, sd, selector, 0, instances)
820
		expectEvents(t, events,
821
			Event{Type: "proxy", ID: "9.9.9.9"},
822
			Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 2},
823
		)
824
	})
825

826
	t.Run("cleanup", func(t *testing.T) {
827
		deleteConfigs([]*config.Config{wle, selector, dnsSelector, dnsWle, wle3}, store, t)
828
		assertControllerEmpty(t, sd)
829
	})
830
}
831

832
func assertControllerEmpty(t *testing.T, sd *Controller) {
833
	assert.Equal(t, len(sd.services.servicesBySE), 0)
834
	assert.Equal(t, len(sd.serviceInstances.ip2instance), 0)
835
	assert.Equal(t, len(sd.serviceInstances.instances), 0)
836
	assert.Equal(t, len(sd.serviceInstances.instancesBySE), 0)
837
	assert.Equal(t, len(sd.serviceInstances.instancesByHostAndPort), 0)
838
	assert.Equal(t, sd.workloadInstances.Empty(), true)
839
}
840

841
func TestServiceDiscoveryWorkloadChangeLabel(t *testing.T) {
842
	store, sd, events := initServiceDiscovery(t)
843

844
	wle := createWorkloadEntry("wl", selector.Name,
845
		&networking.WorkloadEntry{
846
			Address:        "2.2.2.2",
847
			Labels:         map[string]string{"app": "wle"},
848
			ServiceAccount: "default",
849
		})
850

851
	wle2 := createWorkloadEntry("wl", selector.Name,
852
		&networking.WorkloadEntry{
853
			Address:        "2.2.2.2",
854
			Labels:         map[string]string{"app": "wle2"},
855
			ServiceAccount: "default",
856
		})
857
	wle3 := createWorkloadEntry("wl3", selector.Name,
858
		&networking.WorkloadEntry{
859
			Address:        "3.3.3.3",
860
			Labels:         map[string]string{"app": "wle"},
861
			ServiceAccount: "default",
862
		})
863

864
	t.Run("service entry", func(t *testing.T) {
865
		// Add just the ServiceEntry with selector. We should see no instances
866
		createConfigs([]*config.Config{selector}, store, t)
867
		instances := []*model.ServiceInstance{}
868
		expectProxyInstances(t, sd, instances, "2.2.2.2")
869
		expectServiceInstances(t, sd, selector, 0, instances)
870
		expectEvents(t, events,
871
			Event{Type: "service", ID: "selector.com", Namespace: selector.Namespace},
872
			Event{Type: "eds cache", ID: "selector.com", Namespace: selector.Namespace},
873
			Event{Type: "xds full", ID: "selector.com"})
874
	})
875

876
	t.Run("change label removing all", func(t *testing.T) {
877
		// Add a WLE, we expect this to update
878
		createConfigs([]*config.Config{wle}, store, t)
879
		instances := []*model.ServiceInstance{
880
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
881
				selector.Spec.(*networking.ServiceEntry).Ports[0],
882
				map[string]string{"app": "wle"}, "default"),
883
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
884
				selector.Spec.(*networking.ServiceEntry).Ports[1],
885
				map[string]string{"app": "wle"}, "default"),
886
		}
887
		for _, i := range instances {
888
			i.Endpoint.WorkloadName = "wl"
889
			i.Endpoint.Namespace = selector.Name
890
		}
891
		expectProxyInstances(t, sd, instances, "2.2.2.2")
892
		expectServiceInstances(t, sd, selector, 0, instances)
893
		expectEvents(t, events,
894
			Event{Type: "proxy", ID: "2.2.2.2"},
895
			Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 2},
896
		)
897

898
		createConfigs([]*config.Config{wle2}, store, t)
899
		instances = []*model.ServiceInstance{}
900
		expectServiceInstances(t, sd, selector, 0, instances)
901
		expectProxyInstances(t, sd, instances, "2.2.2.2")
902
		expectEvents(t, events,
903
			Event{Type: "proxy", ID: "2.2.2.2"},
904
			Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 0})
905
	})
906

907
	t.Run("change label removing one", func(t *testing.T) {
908
		// Add a WLE, we expect this to update
909
		createConfigs([]*config.Config{wle}, store, t)
910
		expectEvents(t, events,
911
			Event{Type: "proxy", ID: "2.2.2.2"},
912
			Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 2},
913
		)
914
		// add a wle, expect this to be an add
915
		createConfigs([]*config.Config{wle3}, store, t)
916
		instances := []*model.ServiceInstance{
917
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
918
				selector.Spec.(*networking.ServiceEntry).Ports[0],
919
				map[string]string{"app": "wle"}, "default"),
920
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
921
				selector.Spec.(*networking.ServiceEntry).Ports[1],
922
				map[string]string{"app": "wle"}, "default"),
923
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 444,
924
				selector.Spec.(*networking.ServiceEntry).Ports[0],
925
				map[string]string{"app": "wle"}, "default"),
926
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 445,
927
				selector.Spec.(*networking.ServiceEntry).Ports[1],
928
				map[string]string{"app": "wle"}, "default"),
929
		}
930
		for _, i := range instances[:2] {
931
			i.Endpoint.WorkloadName = "wl"
932
			i.Endpoint.Namespace = selector.Name
933
		}
934
		for _, i := range instances[2:] {
935
			i.Endpoint.WorkloadName = "wl3"
936
			i.Endpoint.Namespace = selector.Name
937
		}
938
		expectProxyInstances(t, sd, instances[:2], "2.2.2.2")
939
		expectProxyInstances(t, sd, instances[2:], "3.3.3.3")
940
		expectServiceInstances(t, sd, selector, 0, instances)
941
		expectEvents(t, events,
942
			Event{Type: "proxy", ID: "3.3.3.3"},
943
			Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 4},
944
		)
945

946
		createConfigs([]*config.Config{wle2}, store, t)
947
		instances = []*model.ServiceInstance{
948
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 444,
949
				selector.Spec.(*networking.ServiceEntry).Ports[0],
950
				map[string]string{"app": "wle"}, "default"),
951
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 445,
952
				selector.Spec.(*networking.ServiceEntry).Ports[1],
953
				map[string]string{"app": "wle"}, "default"),
954
		}
955
		for _, i := range instances {
956
			i.Endpoint.WorkloadName = "wl3"
957
			i.Endpoint.Namespace = selector.Name
958
		}
959
		expectServiceInstances(t, sd, selector, 0, instances)
960
		expectProxyInstances(t, sd, instances, "3.3.3.3")
961
		expectEvents(t, events,
962
			Event{Type: "proxy", ID: "2.2.2.2"},
963
			Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 2})
964
	})
965
}
966

967
func TestWorkloadInstanceFullPush(t *testing.T) {
968
	store, sd, events := initServiceDiscovery(t)
969

970
	// Setup a WorkloadEntry with selector the same as ServiceEntry
971
	wle := createWorkloadEntry("wl", selectorDNS.Name,
972
		&networking.WorkloadEntry{
973
			Address:        "postman-echo.com",
974
			Labels:         map[string]string{"app": "wle"},
975
			ServiceAccount: "default",
976
		})
977

978
	fi1 := &model.WorkloadInstance{
979
		Name:      "additional-name",
980
		Namespace: selectorDNS.Name,
981
		Endpoint: &model.IstioEndpoint{
982
			Address:        "4.4.4.4",
983
			Labels:         map[string]string{"app": "wle"},
984
			ServiceAccount: spiffe.MustGenSpiffeURI(selectorDNS.Name, "default"),
985
			TLSMode:        model.IstioMutualTLSModeLabel,
986
		},
987
	}
988

989
	fi2 := &model.WorkloadInstance{
990
		Name:      "another-name",
991
		Namespace: selectorDNS.Namespace,
992
		Endpoint: &model.IstioEndpoint{
993
			Address:        "2.2.2.2",
994
			Labels:         map[string]string{"app": "wle"},
995
			ServiceAccount: spiffe.MustGenSpiffeURI(selectorDNS.Name, "default"),
996
			TLSMode:        model.IstioMutualTLSModeLabel,
997
		},
998
	}
999

1000
	t.Run("service entry", func(t *testing.T) {
1001
		// Add just the ServiceEntry with selector. We should see no instances
1002
		createConfigs([]*config.Config{selectorDNS}, store, t)
1003
		instances := []*model.ServiceInstance{}
1004
		expectProxyInstances(t, sd, instances, "4.4.4.4")
1005
		expectServiceInstances(t, sd, selectorDNS, 0, instances)
1006
		expectEvents(t, events,
1007
			Event{Type: "service", ID: "selector.com", Namespace: selectorDNS.Namespace},
1008
			Event{Type: "eds cache", ID: "selector.com", Namespace: selectorDNS.Namespace},
1009
			Event{Type: "xds full", ID: "selector.com"})
1010
	})
1011

1012
	t.Run("add workload", func(t *testing.T) {
1013
		// Add a WLE, we expect this to update
1014
		createConfigs([]*config.Config{wle}, store, t)
1015

1016
		instances := []*model.ServiceInstance{
1017
			makeInstanceWithServiceAccount(selectorDNS, "postman-echo.com", 444,
1018
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[0],
1019
				map[string]string{"app": "wle"}, "default"),
1020
			makeInstanceWithServiceAccount(selectorDNS, "postman-echo.com", 445,
1021
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[1],
1022
				map[string]string{"app": "wle"}, "default"),
1023
		}
1024
		for _, i := range instances {
1025
			i.Endpoint.WorkloadName = "wl"
1026
			i.Endpoint.Namespace = selectorDNS.Name
1027
		}
1028
		expectProxyInstances(t, sd, instances, "postman-echo.com")
1029
		expectServiceInstances(t, sd, selectorDNS, 0, instances)
1030
		expectEvents(t, events,
1031
			Event{Type: "eds cache", ID: "selector.com", Namespace: selectorDNS.Namespace},
1032
			Event{Type: "xds full", ID: "selector.com"},
1033
		)
1034
	})
1035

1036
	t.Run("full push for new instance", func(t *testing.T) {
1037
		callInstanceHandlers([]*model.WorkloadInstance{fi1}, sd, model.EventAdd, t)
1038
		instances := []*model.ServiceInstance{
1039
			makeInstanceWithServiceAccount(selectorDNS, "4.4.4.4", 444,
1040
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1041
			makeInstanceWithServiceAccount(selectorDNS, "4.4.4.4", 445,
1042
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1043
			makeInstanceWithServiceAccount(selectorDNS, "postman-echo.com", 444,
1044
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1045
			makeInstanceWithServiceAccount(selectorDNS, "postman-echo.com", 445,
1046
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1047
		}
1048

1049
		for _, i := range instances[2:] {
1050
			i.Endpoint.WorkloadName = "wl"
1051
			i.Endpoint.Namespace = selectorDNS.Name
1052
		}
1053

1054
		expectProxyInstances(t, sd, instances[:2], "4.4.4.4")
1055
		expectProxyInstances(t, sd, instances[2:], "postman-echo.com")
1056
		expectServiceInstances(t, sd, selectorDNS, 0, instances)
1057
		expectEvents(t, events,
1058
			Event{Type: "eds", ID: "selector.com", Namespace: selectorDNS.Namespace, EndpointCount: len(instances)},
1059
			Event{Type: "xds full", ID: "selector.com"})
1060
	})
1061

1062
	t.Run("full push for another new workload instance", func(t *testing.T) {
1063
		callInstanceHandlers([]*model.WorkloadInstance{fi2}, sd, model.EventAdd, t)
1064
		expectEvents(t, events,
1065
			Event{Type: "eds", ID: "selector.com", Namespace: selectorDNS.Namespace, EndpointCount: 6},
1066
			Event{Type: "xds full", ID: "selector.com"})
1067
	})
1068

1069
	t.Run("full push on delete workload instance", func(t *testing.T) {
1070
		callInstanceHandlers([]*model.WorkloadInstance{fi1}, sd, model.EventDelete, t)
1071
		instances := []*model.ServiceInstance{
1072
			makeInstanceWithServiceAccount(selectorDNS, "2.2.2.2", 444,
1073
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1074
			makeInstanceWithServiceAccount(selectorDNS, "2.2.2.2", 445,
1075
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1076
			makeInstanceWithServiceAccount(selectorDNS, "postman-echo.com", 444,
1077
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1078
			makeInstanceWithServiceAccount(selectorDNS, "postman-echo.com", 445,
1079
				selectorDNS.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1080
		}
1081

1082
		for _, i := range instances[2:] {
1083
			i.Endpoint.WorkloadName = "wl"
1084
			i.Endpoint.Namespace = selectorDNS.Name
1085
		}
1086

1087
		expectProxyInstances(t, sd, instances[:2], "2.2.2.2")
1088
		expectProxyInstances(t, sd, instances[2:], "postman-echo.com")
1089
		expectServiceInstances(t, sd, selectorDNS, 0, instances)
1090

1091
		expectEvents(t, events,
1092
			Event{Type: "eds", ID: "selector.com", Namespace: selectorDNS.Namespace, EndpointCount: len(instances)},
1093
			Event{Type: "xds full", ID: "selector.com"})
1094
	})
1095
}
1096

1097
func TestServiceDiscoveryWorkloadInstance(t *testing.T) {
1098
	store, sd, events := initServiceDiscovery(t)
1099

1100
	// Setup a couple of workload instances for test. These will be selected by the `selector` SE
1101
	fi1 := &model.WorkloadInstance{
1102
		Name:      selector.Name,
1103
		Namespace: selector.Namespace,
1104
		Endpoint: &model.IstioEndpoint{
1105
			Address:        "2.2.2.2",
1106
			Labels:         map[string]string{"app": "wle"},
1107
			ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"),
1108
			TLSMode:        model.IstioMutualTLSModeLabel,
1109
		},
1110
	}
1111

1112
	fi2 := &model.WorkloadInstance{
1113
		Name:      "some-other-name",
1114
		Namespace: selector.Namespace,
1115
		Endpoint: &model.IstioEndpoint{
1116
			Address:        "3.3.3.3",
1117
			Labels:         map[string]string{"app": "wle"},
1118
			ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"),
1119
			TLSMode:        model.IstioMutualTLSModeLabel,
1120
		},
1121
	}
1122

1123
	fi3 := &model.WorkloadInstance{
1124
		Name:      "another-name",
1125
		Namespace: dnsSelector.Namespace,
1126
		Endpoint: &model.IstioEndpoint{
1127
			Address:        "2.2.2.2",
1128
			Labels:         map[string]string{"app": "dns-wle"},
1129
			ServiceAccount: spiffe.MustGenSpiffeURI(dnsSelector.Name, "default"),
1130
			TLSMode:        model.IstioMutualTLSModeLabel,
1131
		},
1132
	}
1133

1134
	t.Run("service entry", func(t *testing.T) {
1135
		// Add just the ServiceEntry with selector. We should see no instances
1136
		createConfigs([]*config.Config{selector}, store, t)
1137
		instances := []*model.ServiceInstance{}
1138
		expectProxyInstances(t, sd, instances, "2.2.2.2")
1139
		expectServiceInstances(t, sd, selector, 0, instances)
1140
		expectEvents(t, events,
1141
			Event{Type: "service", ID: "selector.com", Namespace: selector.Namespace},
1142
			Event{Type: "eds cache", ID: "selector.com", Namespace: selector.Namespace},
1143
			Event{Type: "xds full", ID: "selector.com"})
1144
	})
1145

1146
	t.Run("add another service entry", func(t *testing.T) {
1147
		createConfigs([]*config.Config{dnsSelector}, store, t)
1148
		instances := []*model.ServiceInstance{}
1149
		expectProxyInstances(t, sd, instances, "2.2.2.2")
1150
		expectServiceInstances(t, sd, dnsSelector, 0, instances)
1151
		expectEvents(t, events,
1152
			Event{Type: "service", ID: "dns.selector.com", Namespace: dnsSelector.Namespace},
1153
			Event{Type: "eds cache", ID: "dns.selector.com", Namespace: dnsSelector.Namespace},
1154
			Event{Type: "xds full", ID: "dns.selector.com"})
1155
	})
1156

1157
	t.Run("add workload instance", func(t *testing.T) {
1158
		// Add a workload instance, we expect this to update
1159
		callInstanceHandlers([]*model.WorkloadInstance{fi1}, sd, model.EventAdd, t)
1160
		instances := []*model.ServiceInstance{
1161
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
1162
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1163
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
1164
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1165
		}
1166
		expectProxyInstances(t, sd, instances, "2.2.2.2")
1167
		expectServiceInstances(t, sd, selector, 0, instances)
1168
		expectEvents(t, events, Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 2})
1169
	})
1170

1171
	t.Run("another workload instance", func(t *testing.T) {
1172
		// Add a different instance
1173
		callInstanceHandlers([]*model.WorkloadInstance{fi2}, sd, model.EventAdd, t)
1174
		instances := []*model.ServiceInstance{
1175
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
1176
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1177
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
1178
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1179
		}
1180
		expectProxyInstances(t, sd, instances, "2.2.2.2")
1181
		instances = append(instances,
1182
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 444,
1183
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1184
			makeInstanceWithServiceAccount(selector, "3.3.3.3", 445,
1185
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"))
1186
		expectServiceInstances(t, sd, selector, 0, instances)
1187
		expectEvents(t, events, Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 4})
1188
	})
1189

1190
	t.Run("delete workload instance", func(t *testing.T) {
1191
		// Delete the instances, it should be gone
1192
		callInstanceHandlers([]*model.WorkloadInstance{fi2}, sd, model.EventDelete, t)
1193
		instances := []*model.ServiceInstance{
1194
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 444,
1195
				selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1196
			makeInstanceWithServiceAccount(selector, "2.2.2.2", 445,
1197
				selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1198
		}
1199
		expectProxyInstances(t, sd, instances, "2.2.2.2")
1200
		expectServiceInstances(t, sd, selector, 0, instances)
1201
		expectEvents(t, events, Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 2})
1202

1203
		key := instancesKey{namespace: selector.Namespace, hostname: "selector.com"}
1204
		namespacedName := selector.NamespacedName()
1205
		if len(sd.serviceInstances.ip2instance) != 1 {
1206
			t.Fatalf("service instances store `ip2instance` memory leak, expect 1, got %d", len(sd.serviceInstances.ip2instance))
1207
		}
1208
		if len(sd.serviceInstances.instances[key]) != 1 {
1209
			t.Fatalf("service instances store `instances` memory leak, expect 1, got %d", len(sd.serviceInstances.instances[key]))
1210
		}
1211
		if len(sd.serviceInstances.instancesBySE[namespacedName]) != 1 {
1212
			t.Fatalf("service instances store `instancesBySE` memory leak, expect 1, got %d", len(sd.serviceInstances.instancesBySE[namespacedName]))
1213
		}
1214

1215
		// The following sections mimic this scenario:
1216
		// f1 starts terminating, f3 picks up the IP, f3 delete event (pod
1217
		// not ready yet) comes before f1
1218
		//
1219
		// Delete f3 event
1220
		callInstanceHandlers([]*model.WorkloadInstance{fi3}, sd, model.EventDelete, t)
1221
		expectProxyInstances(t, sd, instances, "2.2.2.2")
1222
		expectServiceInstances(t, sd, selector, 0, instances)
1223

1224
		// Delete f1 event
1225
		callInstanceHandlers([]*model.WorkloadInstance{fi1}, sd, model.EventDelete, t)
1226
		instances = []*model.ServiceInstance{}
1227
		expectProxyInstances(t, sd, instances, "2.2.2.2")
1228
		expectServiceInstances(t, sd, selector, 0, instances)
1229
		expectEvents(t, events, Event{Type: "eds", ID: "selector.com", Namespace: selector.Namespace, EndpointCount: 0})
1230

1231
		if len(sd.serviceInstances.ip2instance) != 0 {
1232
			t.Fatalf("service instances store `ip2instance` memory leak, expect 0, got %d", len(sd.serviceInstances.ip2instance))
1233
		}
1234
		if len(sd.serviceInstances.instances[key]) != 0 {
1235
			t.Fatalf("service instances store `instances` memory leak, expect 0, got %d", len(sd.serviceInstances.instances[key]))
1236
		}
1237
		if len(sd.serviceInstances.instancesBySE[namespacedName]) != 0 {
1238
			t.Fatalf("service instances store `instancesBySE` memory leak, expect 0, got %d", len(sd.serviceInstances.instancesBySE[namespacedName]))
1239
		}
1240

1241
		// Add f3 event
1242
		callInstanceHandlers([]*model.WorkloadInstance{fi3}, sd, model.EventAdd, t)
1243
		instances = []*model.ServiceInstance{
1244
			makeInstanceWithServiceAccount(dnsSelector, "2.2.2.2", 444,
1245
				dnsSelector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "dns-wle"}, "default"),
1246
			makeInstanceWithServiceAccount(dnsSelector, "2.2.2.2", 445,
1247
				dnsSelector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "dns-wle"}, "default"),
1248
		}
1249
		expectProxyInstances(t, sd, instances, "2.2.2.2")
1250
		expectServiceInstances(t, sd, dnsSelector, 0, instances)
1251
		expectEvents(t, events, Event{Type: "eds", ID: "dns.selector.com", Namespace: dnsSelector.Namespace, EndpointCount: 2})
1252
	})
1253
}
1254

1255
func TestServiceDiscoveryWorkloadInstanceChangeLabel(t *testing.T) {
1256
	store, sd, events := initServiceDiscovery(t)
1257

1258
	type expectedProxyInstances struct {
1259
		instancesWithSA []*model.ServiceInstance
1260
		address         string
1261
	}
1262

1263
	type testWorkloadInstance struct {
1264
		name                   string
1265
		namespace              string
1266
		address                string
1267
		labels                 map[string]string
1268
		serviceAccount         string
1269
		tlsmode                string
1270
		expectedProxyInstances []expectedProxyInstances
1271
	}
1272

1273
	t.Run("service entry", func(t *testing.T) {
1274
		// Add just the ServiceEntry with selector. We should see no instances
1275
		createConfigs([]*config.Config{selector}, store, t)
1276
		instances := []*model.ServiceInstance{}
1277
		expectProxyInstances(t, sd, instances, "2.2.2.2")
1278
		expectServiceInstances(t, sd, selector, 0, instances)
1279
		expectEvents(t, events,
1280
			Event{Type: "service", ID: "selector.com", Namespace: selector.Namespace},
1281
			Event{Type: "eds cache", ID: "selector.com", Namespace: selector.Namespace},
1282
			Event{Type: "xds full"})
1283
	})
1284

1285
	cases := []struct {
1286
		name      string
1287
		instances []testWorkloadInstance
1288
	}{
1289
		{
1290
			name: "change label removing all",
1291
			instances: []testWorkloadInstance{
1292
				{
1293
					name:           selector.Name,
1294
					namespace:      selector.Namespace,
1295
					address:        "2.2.2.2",
1296
					labels:         map[string]string{"app": "wle"},
1297
					serviceAccount: "default",
1298
					tlsmode:        model.IstioMutualTLSModeLabel,
1299
					expectedProxyInstances: []expectedProxyInstances{
1300
						{
1301
							instancesWithSA: []*model.ServiceInstance{
1302
								makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1303
								makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1304
							},
1305
							address: "2.2.2.2",
1306
						},
1307
					},
1308
				},
1309
				{
1310
					name:           selector.Name,
1311
					namespace:      selector.Namespace,
1312
					address:        "2.2.2.2",
1313
					labels:         map[string]string{"app": "wle2"},
1314
					serviceAccount: "default",
1315
					tlsmode:        model.IstioMutualTLSModeLabel,
1316
					expectedProxyInstances: []expectedProxyInstances{
1317
						{
1318
							instancesWithSA: []*model.ServiceInstance{}, // The instance labels don't match the se anymore, so adding this wi removes 2 instances
1319
							address:         "2.2.2.2",
1320
						},
1321
					},
1322
				},
1323
			},
1324
		},
1325
		{
1326
			name: "change label removing all",
1327
			instances: []testWorkloadInstance{
1328
				{
1329
					name:           selector.Name,
1330
					namespace:      selector.Namespace,
1331
					address:        "2.2.2.2",
1332
					labels:         map[string]string{"app": "wle"},
1333
					serviceAccount: "default",
1334
					tlsmode:        model.IstioMutualTLSModeLabel,
1335
					expectedProxyInstances: []expectedProxyInstances{
1336
						{
1337
							instancesWithSA: []*model.ServiceInstance{
1338
								makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1339
								makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1340
							},
1341
							address: "2.2.2.2",
1342
						},
1343
					},
1344
				},
1345
				{
1346
					name:           "another-name",
1347
					namespace:      selector.Namespace,
1348
					address:        "3.3.3.3",
1349
					labels:         map[string]string{"app": "wle"},
1350
					serviceAccount: "default",
1351
					tlsmode:        model.IstioMutualTLSModeLabel,
1352
					expectedProxyInstances: []expectedProxyInstances{
1353
						{
1354
							instancesWithSA: []*model.ServiceInstance{
1355
								makeInstanceWithServiceAccount(selector, "2.2.2.2", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1356
								makeInstanceWithServiceAccount(selector, "2.2.2.2", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1357
							},
1358
							address: "2.2.2.2",
1359
						},
1360
						{
1361
							instancesWithSA: []*model.ServiceInstance{
1362
								makeInstanceWithServiceAccount(selector, "3.3.3.3", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1363
								makeInstanceWithServiceAccount(selector, "3.3.3.3", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1364
							},
1365
							address: "3.3.3.3",
1366
						},
1367
					},
1368
				},
1369
				{
1370
					name:           selector.Name,
1371
					namespace:      selector.Namespace,
1372
					address:        "2.2.2.2",
1373
					labels:         map[string]string{"app": "wle2"},
1374
					serviceAccount: "default",
1375
					tlsmode:        model.IstioMutualTLSModeLabel,
1376
					expectedProxyInstances: []expectedProxyInstances{
1377
						{
1378
							instancesWithSA: []*model.ServiceInstance{
1379
								makeInstanceWithServiceAccount(selector, "3.3.3.3", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"app": "wle"}, "default"),
1380
								makeInstanceWithServiceAccount(selector, "3.3.3.3", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"app": "wle"}, "default"),
1381
							},
1382
							address: "3.3.3.3",
1383
						},
1384
					},
1385
				},
1386
			},
1387
		},
1388
	}
1389

1390
	for _, testCase := range cases {
1391
		t.Run(testCase.name, func(t *testing.T) {
1392
			for _, instance := range testCase.instances {
1393

1394
				wi := &model.WorkloadInstance{
1395
					Name:      instance.name,
1396
					Namespace: instance.namespace,
1397
					Endpoint: &model.IstioEndpoint{
1398
						Address:        instance.address,
1399
						Labels:         instance.labels,
1400
						ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, instance.serviceAccount),
1401
						TLSMode:        instance.tlsmode,
1402
					},
1403
				}
1404

1405
				callInstanceHandlers([]*model.WorkloadInstance{wi}, sd, model.EventAdd, t)
1406

1407
				totalInstances := []*model.ServiceInstance{}
1408
				for _, expectedProxyInstance := range instance.expectedProxyInstances {
1409
					expectProxyInstances(t, sd, expectedProxyInstance.instancesWithSA, expectedProxyInstance.address)
1410
					totalInstances = append(totalInstances, expectedProxyInstance.instancesWithSA...)
1411
				}
1412

1413
				expectServiceInstances(t, sd, selector, 0, totalInstances)
1414
				expectEvents(t, events,
1415
					Event{Type: "eds", ID: selector.Spec.(*networking.ServiceEntry).Hosts[0], Namespace: selector.Namespace, EndpointCount: len(totalInstances)})
1416
			}
1417
		})
1418
	}
1419
}
1420

1421
func expectProxyInstances(t testing.TB, sd *Controller, expected []*model.ServiceInstance, ip string) {
1422
	t.Helper()
1423
	expectProxyTargets(t, sd, slices.Map(expected, model.ServiceInstanceToTarget), ip)
1424
}
1425

1426
func expectProxyTargets(t testing.TB, sd *Controller, expected []model.ServiceTarget, ip string) {
1427
	t.Helper()
1428
	// The system is eventually consistent, so add some retries
1429
	retry.UntilSuccessOrFail(t, func() error {
1430
		instances := sd.GetProxyServiceTargets(&model.Proxy{IPAddresses: []string{ip}, Metadata: &model.NodeMetadata{}})
1431
		sortServiceTargets(instances)
1432
		sortServiceTargets(expected)
1433
		if err := compare(t, instances, expected); err != nil {
1434
			return err
1435
		}
1436
		return nil
1437
	}, retry.Converge(2), retry.Timeout(time.Second*5))
1438
}
1439

1440
func expectEvents(t testing.TB, ch *xdsfake.Updater, events ...Event) {
1441
	t.Helper()
1442
	ch.StrictMatchOrFail(t, events...)
1443
}
1444

1445
func expectServiceInstances(t testing.TB, sd *Controller, cfg *config.Config, port int, expected ...[]*model.ServiceInstance) {
1446
	t.Helper()
1447
	svcs := convertServices(*cfg)
1448
	if len(svcs) != len(expected) {
1449
		t.Fatalf("got more services than expected: %v vs %v", len(svcs), len(expected))
1450
	}
1451
	expe := [][]*model.IstioEndpoint{}
1452
	for _, o := range expected {
1453
		res := []*model.IstioEndpoint{}
1454
		for _, i := range o {
1455
			res = append(res, i.Endpoint)
1456
		}
1457
		expe = append(expe, res)
1458
	}
1459
	// The system is eventually consistent, so add some retries
1460
	retry.UntilSuccessOrFail(t, func() error {
1461
		for i, svc := range svcs {
1462
			endpoints := GetEndpointsForPort(svc, sd.XdsUpdater.(*xdsfake.Updater).Delegate.(*model.EndpointIndexUpdater).Index, port)
1463
			if endpoints == nil {
1464
				endpoints = []*model.IstioEndpoint{} // To simplify tests a bit
1465
			}
1466
			sortEndpoints(endpoints)
1467
			sortEndpoints(expe[i])
1468
			if err := compare(t, endpoints, expe[i]); err != nil {
1469
				return fmt.Errorf("%d: %v", i, err)
1470
			}
1471
		}
1472
		return nil
1473
	}, retry.Converge(2), retry.Timeout(time.Second*1))
1474
}
1475

1476
func TestServiceDiscoveryGetProxyServiceTargets(t *testing.T) {
1477
	store, sd, _ := initServiceDiscovery(t)
1478

1479
	createConfigs([]*config.Config{httpStatic, tcpStatic}, store, t)
1480

1481
	expectProxyInstances(t, sd, []*model.ServiceInstance{
1482
		makeInstance(httpStatic, "2.2.2.2", 7080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
1483
		makeInstance(httpStatic, "2.2.2.2", 18080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS),
1484
		makeInstance(tcpStatic, "2.2.2.2", 444, tcpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
1485
	}, "2.2.2.2")
1486
}
1487

1488
// Keeping this test for legacy - but it never happens in real life.
1489
func TestServiceDiscoveryInstances(t *testing.T) {
1490
	store, sd, _ := initServiceDiscovery(t)
1491

1492
	createConfigs([]*config.Config{httpDNS, tcpStatic}, store, t)
1493

1494
	expectServiceInstances(t, sd, httpDNS, 0, []*model.ServiceInstance{
1495
		makeInstance(httpDNS, "us.google.com", 7080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
1496
		makeInstance(httpDNS, "us.google.com", 18080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS),
1497
		makeInstance(httpDNS, "uk.google.com", 1080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
1498
		makeInstance(httpDNS, "uk.google.com", 8080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS),
1499
		makeInstance(httpDNS, "de.google.com", 80, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, MTLS),
1500
		makeInstance(httpDNS, "de.google.com", 8080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"foo": "bar"}, MTLS),
1501
	})
1502
}
1503

1504
// Keeping this test for legacy - but it never happens in real life.
1505
func TestServiceDiscoveryInstances1Port(t *testing.T) {
1506
	store, sd, _ := initServiceDiscovery(t)
1507

1508
	createConfigs([]*config.Config{httpDNS, tcpStatic}, store, t)
1509

1510
	expectServiceInstances(t, sd, httpDNS, 80, []*model.ServiceInstance{
1511
		makeInstance(httpDNS, "us.google.com", 7080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
1512
		makeInstance(httpDNS, "uk.google.com", 1080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
1513
		makeInstance(httpDNS, "de.google.com", 80, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, MTLS),
1514
	})
1515
}
1516

1517
func TestNonServiceConfig(t *testing.T) {
1518
	store, sd, _ := initServiceDiscovery(t)
1519

1520
	// Create a non-service configuration element. This should not affect the service registry at all.
1521
	cfg := config.Config{
1522
		Meta: config.Meta{
1523
			GroupVersionKind:  gvk.DestinationRule,
1524
			Name:              "fakeDestinationRule",
1525
			Namespace:         "default",
1526
			Domain:            "cluster.local",
1527
			CreationTimestamp: GlobalTime,
1528
		},
1529
		Spec: &networking.DestinationRule{
1530
			Host: "fakehost",
1531
		},
1532
	}
1533
	_, err := store.Create(cfg)
1534
	if err != nil {
1535
		t.Errorf("error occurred crearting ServiceEntry config: %v", err)
1536
	}
1537

1538
	// Now create some service entries and verify that it's added to the registry.
1539
	createConfigs([]*config.Config{httpDNS, tcpStatic}, store, t)
1540
	expectServiceInstances(t, sd, httpDNS, 80, []*model.ServiceInstance{
1541
		makeInstance(httpDNS, "us.google.com", 7080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
1542
		makeInstance(httpDNS, "uk.google.com", 1080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
1543
		makeInstance(httpDNS, "de.google.com", 80, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, MTLS),
1544
	})
1545
}
1546

1547
// nolint: lll
1548
func TestServicesDiff(t *testing.T) {
1549
	updatedHTTPDNS := &config.Config{
1550
		Meta: config.Meta{
1551
			GroupVersionKind:  gvk.ServiceEntry,
1552
			Name:              "httpDNS",
1553
			Namespace:         "httpDNS",
1554
			CreationTimestamp: GlobalTime,
1555
			Labels:            map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
1556
		},
1557
		Spec: &networking.ServiceEntry{
1558
			Hosts: []string{"*.google.com", "*.mail.com"},
1559
			Ports: []*networking.ServicePort{
1560
				{Number: 80, Name: "http-port", Protocol: "http"},
1561
				{Number: 8080, Name: "http-alt-port", Protocol: "http"},
1562
			},
1563
			Endpoints: []*networking.WorkloadEntry{
1564
				{
1565
					Address: "us.google.com",
1566
					Ports:   map[string]uint32{"http-port": 7080, "http-alt-port": 18080},
1567
					Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
1568
				},
1569
				{
1570
					Address: "uk.google.com",
1571
					Ports:   map[string]uint32{"http-port": 1080},
1572
					Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
1573
				},
1574
				{
1575
					Address: "de.google.com",
1576
					Labels:  map[string]string{"foo": "bar", label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
1577
				},
1578
			},
1579
			Location:   networking.ServiceEntry_MESH_EXTERNAL,
1580
			Resolution: networking.ServiceEntry_DNS,
1581
		},
1582
	}
1583

1584
	updatedHTTPDNSPort := func() *config.Config {
1585
		c := updatedHTTPDNS.DeepCopy()
1586
		se := c.Spec.(*networking.ServiceEntry)
1587
		var ports []*networking.ServicePort
1588
		ports = append(ports, se.Ports...)
1589
		ports = append(ports, &networking.ServicePort{Number: 9090, Name: "http-new-port", Protocol: "http"})
1590
		se.Ports = ports
1591
		return &c
1592
	}()
1593

1594
	updatedEndpoint := func() *config.Config {
1595
		c := updatedHTTPDNS.DeepCopy()
1596
		se := c.Spec.(*networking.ServiceEntry)
1597
		var endpoints []*networking.WorkloadEntry
1598
		endpoints = append(endpoints, se.Endpoints...)
1599
		endpoints = append(endpoints, &networking.WorkloadEntry{
1600
			Address: "in.google.com",
1601
			Labels:  map[string]string{"foo": "bar", label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
1602
		})
1603
		se.Endpoints = endpoints
1604
		return &c
1605
	}()
1606

1607
	stringsToHosts := func(hosts []string) []host.Name {
1608
		ret := make([]host.Name, len(hosts))
1609
		for i, hostname := range hosts {
1610
			ret[i] = host.Name(hostname)
1611
		}
1612
		return ret
1613
	}
1614

1615
	cases := []struct {
1616
		name    string
1617
		current *config.Config
1618
		new     *config.Config
1619

1620
		added     []host.Name
1621
		deleted   []host.Name
1622
		updated   []host.Name
1623
		unchanged []host.Name
1624
	}{
1625
		{
1626
			name:      "same config",
1627
			current:   updatedHTTPDNS,
1628
			new:       updatedHTTPDNS,
1629
			unchanged: stringsToHosts(updatedHTTPDNS.Spec.(*networking.ServiceEntry).Hosts),
1630
		},
1631
		{
1632
			name:    "different resolution",
1633
			current: updatedHTTPDNS,
1634
			new: func() *config.Config {
1635
				c := updatedHTTPDNS.DeepCopy()
1636
				c.Spec.(*networking.ServiceEntry).Resolution = networking.ServiceEntry_NONE
1637
				return &c
1638
			}(),
1639
			updated: stringsToHosts(updatedHTTPDNS.Spec.(*networking.ServiceEntry).Hosts),
1640
		},
1641
		{
1642
			name:    "config modified with added/deleted host",
1643
			current: updatedHTTPDNS,
1644
			new: func() *config.Config {
1645
				c := updatedHTTPDNS.DeepCopy()
1646
				se := c.Spec.(*networking.ServiceEntry)
1647
				se.Hosts = []string{"*.google.com", "host.com"}
1648
				return &c
1649
			}(),
1650
			added:     []host.Name{"host.com"},
1651
			deleted:   []host.Name{"*.mail.com"},
1652
			unchanged: []host.Name{"*.google.com"},
1653
		},
1654
		{
1655
			name:    "config modified with additional port",
1656
			current: updatedHTTPDNS,
1657
			new:     updatedHTTPDNSPort,
1658
			updated: stringsToHosts(updatedHTTPDNS.Spec.(*networking.ServiceEntry).Hosts),
1659
		},
1660
		{
1661
			name:      "same config with additional endpoint",
1662
			current:   updatedHTTPDNS,
1663
			new:       updatedEndpoint,
1664
			unchanged: stringsToHosts(updatedHTTPDNS.Spec.(*networking.ServiceEntry).Hosts),
1665
		},
1666
	}
1667

1668
	servicesHostnames := func(services []*model.Service) []host.Name {
1669
		if len(services) == 0 {
1670
			return nil
1671
		}
1672
		ret := make([]host.Name, len(services))
1673
		for i, svc := range services {
1674
			ret[i] = svc.Hostname
1675
		}
1676
		return ret
1677
	}
1678

1679
	for _, tt := range cases {
1680
		t.Run(tt.name, func(t *testing.T) {
1681
			as := convertServices(*tt.current)
1682
			bs := convertServices(*tt.new)
1683
			added, deleted, updated, unchanged := servicesDiff(as, bs)
1684
			for i, item := range []struct {
1685
				hostnames []host.Name
1686
				services  []*model.Service
1687
			}{
1688
				{tt.added, added},
1689
				{tt.deleted, deleted},
1690
				{tt.updated, updated},
1691
				{tt.unchanged, unchanged},
1692
			} {
1693
				if !reflect.DeepEqual(servicesHostnames(item.services), item.hostnames) {
1694
					t.Errorf("ServicesChanged %d got %v, want %v", i, servicesHostnames(item.services), item.hostnames)
1695
				}
1696
			}
1697
		})
1698
	}
1699
}
1700

1701
func sortServices(services []*model.Service) {
1702
	sort.Slice(services, func(i, j int) bool { return services[i].Hostname < services[j].Hostname })
1703
	for _, service := range services {
1704
		sortPorts(service.Ports)
1705
	}
1706
}
1707

1708
func sortServiceTargets(instances []model.ServiceTarget) {
1709
	sort.Slice(instances, func(i, j int) bool {
1710
		if instances[i].Service.Hostname == instances[j].Service.Hostname {
1711
			if instances[i].Port.TargetPort == instances[j].Port.TargetPort {
1712
				return instances[i].Port.TargetPort < instances[j].Port.TargetPort
1713
			}
1714
		}
1715
		return instances[i].Service.Hostname < instances[j].Service.Hostname
1716
	})
1717
}
1718

1719
func sortServiceInstances(instances []*model.ServiceInstance) {
1720
	labelsToSlice := func(labels labels.Instance) []string {
1721
		out := make([]string, 0, len(labels))
1722
		for k, v := range labels {
1723
			out = append(out, fmt.Sprintf("%s=%s", k, v))
1724
		}
1725
		sort.Strings(out)
1726
		return out
1727
	}
1728

1729
	sort.Slice(instances, func(i, j int) bool {
1730
		if instances[i].Service.Hostname == instances[j].Service.Hostname {
1731
			if instances[i].Endpoint.EndpointPort == instances[j].Endpoint.EndpointPort {
1732
				if instances[i].Endpoint.Address == instances[j].Endpoint.Address {
1733
					if len(instances[i].Endpoint.Labels) == len(instances[j].Endpoint.Labels) {
1734
						iLabels := labelsToSlice(instances[i].Endpoint.Labels)
1735
						jLabels := labelsToSlice(instances[j].Endpoint.Labels)
1736
						for k := range iLabels {
1737
							if iLabels[k] < jLabels[k] {
1738
								return true
1739
							}
1740
						}
1741
					}
1742
					return len(instances[i].Endpoint.Labels) < len(instances[j].Endpoint.Labels)
1743
				}
1744
				return instances[i].Endpoint.Address < instances[j].Endpoint.Address
1745
			}
1746
			return instances[i].Endpoint.EndpointPort < instances[j].Endpoint.EndpointPort
1747
		}
1748
		return instances[i].Service.Hostname < instances[j].Service.Hostname
1749
	})
1750
}
1751

1752
func sortEndpoints(endpoints []*model.IstioEndpoint) {
1753
	labelsToSlice := func(labels labels.Instance) []string {
1754
		out := make([]string, 0, len(labels))
1755
		for k, v := range labels {
1756
			out = append(out, fmt.Sprintf("%s=%s", k, v))
1757
		}
1758
		sort.Strings(out)
1759
		return out
1760
	}
1761

1762
	sort.Slice(endpoints, func(i, j int) bool {
1763
		if endpoints[i].EndpointPort == endpoints[j].EndpointPort {
1764
			if endpoints[i].Address == endpoints[j].Address {
1765
				if len(endpoints[i].Labels) == len(endpoints[j].Labels) {
1766
					iLabels := labelsToSlice(endpoints[i].Labels)
1767
					jLabels := labelsToSlice(endpoints[j].Labels)
1768
					for k := range iLabels {
1769
						if iLabels[k] < jLabels[k] {
1770
							return true
1771
						}
1772
					}
1773
				}
1774
				return len(endpoints[i].Labels) < len(endpoints[j].Labels)
1775
			}
1776
			return endpoints[i].Address < endpoints[j].Address
1777
		}
1778
		return endpoints[i].EndpointPort < endpoints[j].EndpointPort
1779
	})
1780
}
1781

1782
func sortPorts(ports []*model.Port) {
1783
	sort.Slice(ports, func(i, j int) bool {
1784
		if ports[i].Port == ports[j].Port {
1785
			if ports[i].Name == ports[j].Name {
1786
				return ports[i].Protocol < ports[j].Protocol
1787
			}
1788
			return ports[i].Name < ports[j].Name
1789
		}
1790
		return ports[i].Port < ports[j].Port
1791
	})
1792
}
1793

1794
func Test_autoAllocateIP_conditions(t *testing.T) {
1795
	tests := []struct {
1796
		name         string
1797
		inServices   []*model.Service
1798
		wantServices []*model.Service
1799
	}{
1800
		{
1801
			name: "no allocation for passthrough",
1802
			inServices: []*model.Service{
1803
				{
1804
					Hostname:       "foo.com",
1805
					Resolution:     model.Passthrough,
1806
					DefaultAddress: "0.0.0.0",
1807
				},
1808
			},
1809
			wantServices: []*model.Service{
1810
				{
1811
					Hostname:       "foo.com",
1812
					Resolution:     model.Passthrough,
1813
					DefaultAddress: "0.0.0.0",
1814
				},
1815
			},
1816
		},
1817
		{
1818
			name: "no allocation if address exists",
1819
			inServices: []*model.Service{
1820
				{
1821
					Hostname:       "foo.com",
1822
					Resolution:     model.ClientSideLB,
1823
					DefaultAddress: "1.1.1.1",
1824
				},
1825
			},
1826
			wantServices: []*model.Service{
1827
				{
1828
					Hostname:       "foo.com",
1829
					Resolution:     model.ClientSideLB,
1830
					DefaultAddress: "1.1.1.1",
1831
				},
1832
			},
1833
		},
1834
		{
1835
			name: "no allocation if hostname is wildcard",
1836
			inServices: []*model.Service{
1837
				{
1838
					Hostname:       "*.foo.com",
1839
					Resolution:     model.ClientSideLB,
1840
					DefaultAddress: "1.1.1.1",
1841
				},
1842
			},
1843
			wantServices: []*model.Service{
1844
				{
1845
					Hostname:       "*.foo.com",
1846
					Resolution:     model.ClientSideLB,
1847
					DefaultAddress: "1.1.1.1",
1848
				},
1849
			},
1850
		},
1851
		{
1852
			name: "allocate IP for clientside lb",
1853
			inServices: []*model.Service{
1854
				{
1855
					Hostname:       "foo.com",
1856
					Resolution:     model.ClientSideLB,
1857
					DefaultAddress: "0.0.0.0",
1858
				},
1859
			},
1860
			wantServices: []*model.Service{
1861
				{
1862
					Hostname:                 "foo.com",
1863
					Resolution:               model.ClientSideLB,
1864
					DefaultAddress:           "0.0.0.0",
1865
					AutoAllocatedIPv4Address: "240.240.227.81",
1866
					AutoAllocatedIPv6Address: "2001:2::f0f0:e351",
1867
				},
1868
			},
1869
		},
1870
		{
1871
			name: "allocate IP for dns lb",
1872
			inServices: []*model.Service{
1873
				{
1874
					Hostname:       "foo.com",
1875
					Resolution:     model.DNSLB,
1876
					DefaultAddress: "0.0.0.0",
1877
				},
1878
			},
1879
			wantServices: []*model.Service{
1880
				{
1881
					Hostname:                 "foo.com",
1882
					Resolution:               model.DNSLB,
1883
					DefaultAddress:           "0.0.0.0",
1884
					AutoAllocatedIPv4Address: "240.240.227.81",
1885
					AutoAllocatedIPv6Address: "2001:2::f0f0:e351",
1886
				},
1887
			},
1888
		},
1889
		{
1890
			name: "collision",
1891
			inServices: []*model.Service{
1892
				{
1893
					Hostname:       "a17061.example.com",
1894
					Resolution:     model.DNSLB,
1895
					DefaultAddress: "0.0.0.0",
1896
				},
1897
				{
1898
					// hashes to the same value as the hostname above,
1899
					// a new collision needs to be found if the hash algorithm changes
1900
					Hostname:       "a44155.example.com",
1901
					Resolution:     model.DNSLB,
1902
					DefaultAddress: "0.0.0.0",
1903
				},
1904
			},
1905
			wantServices: []*model.Service{
1906
				{
1907
					Hostname:                 "a17061.example.com",
1908
					Resolution:               model.DNSLB,
1909
					DefaultAddress:           "0.0.0.0",
1910
					AutoAllocatedIPv4Address: "240.240.25.11",
1911
					AutoAllocatedIPv6Address: "2001:2::f0f0:19b",
1912
				},
1913
				{
1914
					Hostname:                 "a44155.example.com",
1915
					Resolution:               model.DNSLB,
1916
					DefaultAddress:           "0.0.0.0",
1917
					AutoAllocatedIPv4Address: "240.240.31.17",
1918
					AutoAllocatedIPv6Address: "2001:2::f0f0:1f11",
1919
				},
1920
			},
1921
		},
1922
		{
1923
			name: "stable IP - baseline test",
1924
			inServices: []*model.Service{
1925
				{
1926
					Hostname:       "a.example.com",
1927
					Resolution:     model.DNSLB,
1928
					DefaultAddress: "0.0.0.0",
1929
					Attributes:     model.ServiceAttributes{Namespace: "a"},
1930
				},
1931
			},
1932
			wantServices: []*model.Service{
1933
				{
1934
					Hostname:                 "a.example.com",
1935
					Resolution:               model.DNSLB,
1936
					DefaultAddress:           "0.0.0.0",
1937
					AutoAllocatedIPv4Address: "240.240.134.206",
1938
					AutoAllocatedIPv6Address: "2001:2::f0f0:86ce",
1939
				},
1940
			},
1941
		},
1942
		{
1943
			name: "stable IP - not affected by other namespace",
1944
			inServices: []*model.Service{
1945
				{
1946
					Hostname:       "a.example.com",
1947
					Resolution:     model.DNSLB,
1948
					DefaultAddress: "0.0.0.0",
1949
					Attributes:     model.ServiceAttributes{Namespace: "a"},
1950
				},
1951
				{
1952
					Hostname:       "a.example.com",
1953
					Resolution:     model.DNSLB,
1954
					DefaultAddress: "0.0.0.0",
1955
					Attributes:     model.ServiceAttributes{Namespace: "b"},
1956
				},
1957
			},
1958
			wantServices: []*model.Service{
1959
				{
1960
					Hostname:                 "a.example.com",
1961
					Resolution:               model.DNSLB,
1962
					DefaultAddress:           "0.0.0.0",
1963
					AutoAllocatedIPv4Address: "240.240.134.206",
1964
					AutoAllocatedIPv6Address: "2001:2::f0f0:86ce",
1965
				},
1966
				{
1967
					Hostname:                 "a.example.com",
1968
					Resolution:               model.DNSLB,
1969
					DefaultAddress:           "0.0.0.0",
1970
					AutoAllocatedIPv4Address: "240.240.41.100",
1971
					AutoAllocatedIPv6Address: "2001:2::f0f0:2964",
1972
				},
1973
			},
1974
		},
1975
	}
1976
	for _, tt := range tests {
1977
		t.Run(tt.name, func(t *testing.T) {
1978
			gotServices := autoAllocateIPs(tt.inServices)
1979
			for i, got := range gotServices {
1980
				if got.AutoAllocatedIPv4Address != tt.wantServices[i].AutoAllocatedIPv4Address {
1981
					t.Errorf("autoAllocateIPs() AutoAllocatedIPv4Address = %v, want %v",
1982
						got.AutoAllocatedIPv4Address, tt.wantServices[i].AutoAllocatedIPv4Address)
1983
				}
1984
				if got.AutoAllocatedIPv6Address != tt.wantServices[i].AutoAllocatedIPv6Address {
1985
					t.Errorf("autoAllocateIPs() AutoAllocatedIPv6Address = %v, want %v",
1986
						got.AutoAllocatedIPv6Address, tt.wantServices[i].AutoAllocatedIPv6Address)
1987
				}
1988
			}
1989
		})
1990
	}
1991
}
1992

1993
func Test_autoAllocateIP_values(t *testing.T) {
1994
	ips := maxIPs
1995
	inServices := make([]*model.Service, ips)
1996
	for i := 0; i < ips; i++ {
1997
		temp := model.Service{
1998
			Hostname:       host.Name(fmt.Sprintf("foo%d.com", i)),
1999
			Resolution:     model.ClientSideLB,
2000
			DefaultAddress: constants.UnspecifiedIP,
2001
		}
2002
		inServices[i] = &temp
2003
	}
2004
	gotServices := autoAllocateIPs(inServices)
2005

2006
	// We dont expect the following pattern of IPs.
2007
	// 240.240.0.0
2008
	// 240.240.0.255
2009
	// 240.240.1.0
2010
	// 240.240.1.255
2011
	// 240.240.2.0
2012
	// 240.240.2.255
2013
	// 240.240.3.0
2014
	// 240.240.3.255
2015
	// The last IP should be 240.240.202.167
2016
	doNotWant := map[string]bool{
2017
		"240.240.0.0":   true,
2018
		"240.240.0.255": true,
2019
		"240.240.1.0":   true,
2020
		"240.240.1.255": true,
2021
		"240.240.2.0":   true,
2022
		"240.240.2.255": true,
2023
		"240.240.3.0":   true,
2024
		"240.240.3.255": true,
2025
	}
2026
	expectedLastIP := "240.240.10.222"
2027
	if gotServices[len(gotServices)-1].AutoAllocatedIPv4Address != expectedLastIP {
2028
		t.Errorf("expected last IP address to be %s, got %s", expectedLastIP, gotServices[len(gotServices)-1].AutoAllocatedIPv4Address)
2029
	}
2030

2031
	gotIPMap := make(map[string]string)
2032
	for _, svc := range gotServices {
2033
		if svc.AutoAllocatedIPv4Address == "" || doNotWant[svc.AutoAllocatedIPv4Address] {
2034
			t.Errorf("unexpected value for auto allocated IP address %s for service %s", svc.AutoAllocatedIPv4Address, svc.Hostname.String())
2035
		}
2036
		if v, ok := gotIPMap[svc.AutoAllocatedIPv4Address]; ok && v != svc.Hostname.String() {
2037
			t.Errorf("multiple allocations of same IP address to different services with different hostname: %s", svc.AutoAllocatedIPv4Address)
2038
		}
2039
		gotIPMap[svc.AutoAllocatedIPv4Address] = svc.Hostname.String()
2040
		// Validate that IP address is valid.
2041
		ip := net.ParseIP(svc.AutoAllocatedIPv4Address)
2042
		if ip == nil {
2043
			t.Errorf("invalid IP address %s : %s", svc.AutoAllocatedIPv4Address, svc.Hostname.String())
2044
		}
2045
		// Validate that IP address is in the expected range.
2046
		_, subnet, _ := net.ParseCIDR("240.240.0.0/16")
2047
		if !subnet.Contains(ip) {
2048
			t.Errorf("IP address not in range %s : %s", svc.AutoAllocatedIPv4Address, svc.Hostname.String())
2049
		}
2050
	}
2051
	assert.Equal(t, maxIPs, len(gotIPMap))
2052
}
2053

2054
func BenchmarkAutoAllocateIPs(t *testing.B) {
2055
	inServices := make([]*model.Service, 255*255)
2056
	for i := 0; i < 255*255; i++ {
2057
		temp := model.Service{
2058
			Hostname:       host.Name(fmt.Sprintf("foo%d.com", i)),
2059
			Resolution:     model.ClientSideLB,
2060
			DefaultAddress: constants.UnspecifiedIP,
2061
		}
2062
		inServices[i] = &temp
2063
	}
2064
	t.ResetTimer()
2065
	for i := 0; i < t.N; i++ {
2066
		autoAllocateIPs(inServices)
2067
	}
2068
}
2069

2070
// Validate that ipaddress allocation is deterministic based on hash.
2071
func Test_autoAllocateIP_deterministic(t *testing.T) {
2072
	inServices := make([]*model.Service, 0)
2073
	originalServices := map[string]string{
2074
		"a.com": "240.240.109.8",
2075
		"c.com": "240.240.234.51",
2076
		"e.com": "240.240.85.60",
2077
		"g.com": "240.240.23.172",
2078
		"i.com": "240.240.15.2",
2079
		"k.com": "240.240.160.161",
2080
		"l.com": "240.240.42.96",
2081
		"n.com": "240.240.121.61",
2082
		"o.com": "240.240.122.71",
2083
	}
2084

2085
	allocateAndValidate := func() {
2086
		gotServices := autoAllocateIPs(model.SortServicesByCreationTime(inServices))
2087
		gotIPMap := make(map[string]string)
2088
		serviceIPMap := make(map[string]string)
2089
		for _, svc := range gotServices {
2090
			if v, ok := gotIPMap[svc.AutoAllocatedIPv4Address]; ok && v != svc.Hostname.String() {
2091
				t.Errorf("multiple allocations of same IP address to different services with different hostname: %s", svc.AutoAllocatedIPv4Address)
2092
			}
2093
			gotIPMap[svc.AutoAllocatedIPv4Address] = svc.Hostname.String()
2094
			serviceIPMap[svc.Hostname.String()] = svc.AutoAllocatedIPv4Address
2095
		}
2096
		for k, v := range originalServices {
2097
			if gotIPMap[v] != k {
2098
				t.Errorf("ipaddress changed for service %s. expected: %s, got: %s", k, v, serviceIPMap[k])
2099
			}
2100
		}
2101
		for k, v := range gotIPMap {
2102
			if net.ParseIP(k) == nil {
2103
				t.Errorf("invalid ipaddress for service %s. got: %s", v, k)
2104
			}
2105
		}
2106
	}
2107

2108
	// Validate that IP addresses are allocated for original list of services.
2109
	for k := range originalServices {
2110
		inServices = append(inServices, &model.Service{
2111
			Hostname:       host.Name(k),
2112
			Resolution:     model.ClientSideLB,
2113
			DefaultAddress: constants.UnspecifiedIP,
2114
		})
2115
	}
2116
	allocateAndValidate()
2117

2118
	// Now add few services in between and validate that IPs are retained for original services.
2119
	addServices := map[string]bool{
2120
		"b.com": true,
2121
		"d.com": true,
2122
		"f.com": true,
2123
		"h.com": true,
2124
		"j.com": true,
2125
		"m.com": true,
2126
		"p.com": true,
2127
		"q.com": true,
2128
		"r.com": true,
2129
	}
2130

2131
	for k := range addServices {
2132
		inServices = append(inServices, &model.Service{
2133
			Hostname:       host.Name(k),
2134
			Resolution:     model.ClientSideLB,
2135
			DefaultAddress: constants.UnspecifiedIP,
2136
		})
2137
	}
2138
	allocateAndValidate()
2139

2140
	// Now delete few services and validate that IPs are retained for original services.
2141
	deleteServices := []*model.Service{}
2142
	for i, svc := range inServices {
2143
		if _, exists := originalServices[svc.Hostname.String()]; !exists {
2144
			if i%2 == 0 {
2145
				continue
2146
			}
2147
		}
2148
		deleteServices = append(deleteServices, svc)
2149
	}
2150
	inServices = deleteServices
2151
	allocateAndValidate()
2152
}
2153

2154
func Test_autoAllocateIP_with_duplicated_host(t *testing.T) {
2155
	inServices := make([]*model.Service, 0)
2156
	originalServices := map[string]string{
2157
		"a.com": "240.240.109.8",
2158
		"c.com": "240.240.234.51",
2159
		"e.com": "240.240.85.60",
2160
		"g.com": "240.240.23.172",
2161
		"i.com": "240.240.15.2",
2162
		"k.com": "240.240.160.161",
2163
		"l.com": "240.240.42.96",
2164
		"n.com": "240.240.121.61",
2165
		"o.com": "240.240.122.71",
2166
	}
2167

2168
	allocateAndValidate := func() {
2169
		gotServices := autoAllocateIPs(model.SortServicesByCreationTime(inServices))
2170
		gotIPMap := make(map[string]string)
2171
		serviceIPMap := make(map[string]string)
2172
		for _, svc := range gotServices {
2173
			if v, ok := gotIPMap[svc.AutoAllocatedIPv4Address]; ok && v != svc.Hostname.String() {
2174
				t.Errorf("multiple allocations of same IP address to different services with different hostname: %s", svc.AutoAllocatedIPv4Address)
2175
			}
2176
			gotIPMap[svc.AutoAllocatedIPv4Address] = svc.Hostname.String()
2177
			serviceIPMap[svc.Hostname.String()] = svc.AutoAllocatedIPv4Address
2178
		}
2179
		for k, v := range originalServices {
2180
			if gotIPMap[v] != k {
2181
				t.Errorf("ipaddress changed for service %s. expected: %s, got: %s", k, v, serviceIPMap[k])
2182
			}
2183
		}
2184
		for k, v := range gotIPMap {
2185
			if net.ParseIP(k) == nil {
2186
				t.Errorf("invalid ipaddress for service %s. got: %s", v, k)
2187
			}
2188
		}
2189
	}
2190

2191
	// Validate that IP addresses are allocated for original list of services.
2192
	for k := range originalServices {
2193
		inServices = append(inServices, &model.Service{
2194
			Hostname:       host.Name(k),
2195
			Resolution:     model.ClientSideLB,
2196
			DefaultAddress: constants.UnspecifiedIP,
2197
		})
2198
	}
2199
	allocateAndValidate()
2200

2201
	// Now add service with duplicated hostname validate that IPs are retained for original services and duplicated reuse the same IP
2202
	addServices := map[string]bool{
2203
		"i.com": true,
2204
	}
2205

2206
	for k := range addServices {
2207
		inServices = append(inServices, &model.Service{
2208
			Hostname:       host.Name(k),
2209
			Resolution:     model.ClientSideLB,
2210
			DefaultAddress: constants.UnspecifiedIP,
2211
		})
2212
	}
2213
	allocateAndValidate()
2214
}
2215

2216
func TestWorkloadEntryOnlyMode(t *testing.T) {
2217
	store, registry, _ := initServiceDiscoveryWithOpts(t, true)
2218
	createConfigs([]*config.Config{httpStatic}, store, t)
2219
	svcs := registry.Services()
2220
	if len(svcs) > 0 {
2221
		t.Fatalf("expected 0 services, got %d", len(svcs))
2222
	}
2223
	svc := registry.GetService("*.google.com")
2224
	if svc != nil {
2225
		t.Fatalf("expected nil, got %v", svc)
2226
	}
2227
}
2228

2229
func BenchmarkServiceEntryHandler(b *testing.B) {
2230
	_, sd := initServiceDiscoveryWithoutEvents(b)
2231
	stopCh := make(chan struct{})
2232
	go sd.Run(stopCh)
2233
	defer close(stopCh)
2234
	for i := 0; i < b.N; i++ {
2235
		sd.serviceEntryHandler(config.Config{}, *httpDNS, model.EventAdd)
2236
		sd.serviceEntryHandler(config.Config{}, *httpDNSRR, model.EventAdd)
2237
		sd.serviceEntryHandler(config.Config{}, *tcpDNS, model.EventAdd)
2238
		sd.serviceEntryHandler(config.Config{}, *tcpStatic, model.EventAdd)
2239

2240
		sd.serviceEntryHandler(config.Config{}, *httpDNS, model.EventDelete)
2241
		sd.serviceEntryHandler(config.Config{}, *httpDNSRR, model.EventDelete)
2242
		sd.serviceEntryHandler(config.Config{}, *tcpDNS, model.EventDelete)
2243
		sd.serviceEntryHandler(config.Config{}, *tcpStatic, model.EventDelete)
2244
	}
2245
}
2246

2247
func BenchmarkWorkloadInstanceHandler(b *testing.B) {
2248
	store, sd := initServiceDiscoveryWithoutEvents(b)
2249
	stopCh := make(chan struct{})
2250
	go sd.Run(stopCh)
2251
	defer close(stopCh)
2252
	// Add just the ServiceEntry with selector. We should see no instances
2253
	createConfigs([]*config.Config{selector, dnsSelector}, store, b)
2254

2255
	// Setup a couple of workload instances for test. These will be selected by the `selector` SE
2256
	fi1 := &model.WorkloadInstance{
2257
		Name:      selector.Name,
2258
		Namespace: selector.Namespace,
2259
		Endpoint: &model.IstioEndpoint{
2260
			Address:        "2.2.2.2",
2261
			Labels:         map[string]string{"app": "wle"},
2262
			ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"),
2263
			TLSMode:        model.IstioMutualTLSModeLabel,
2264
		},
2265
	}
2266

2267
	fi2 := &model.WorkloadInstance{
2268
		Name:      "some-other-name",
2269
		Namespace: selector.Namespace,
2270
		Endpoint: &model.IstioEndpoint{
2271
			Address:        "3.3.3.3",
2272
			Labels:         map[string]string{"app": "wle"},
2273
			ServiceAccount: spiffe.MustGenSpiffeURI(selector.Name, "default"),
2274
			TLSMode:        model.IstioMutualTLSModeLabel,
2275
		},
2276
	}
2277

2278
	fi3 := &model.WorkloadInstance{
2279
		Name:      "another-name",
2280
		Namespace: dnsSelector.Namespace,
2281
		Endpoint: &model.IstioEndpoint{
2282
			Address:        "2.2.2.2",
2283
			Labels:         map[string]string{"app": "dns-wle"},
2284
			ServiceAccount: spiffe.MustGenSpiffeURI(dnsSelector.Name, "default"),
2285
			TLSMode:        model.IstioMutualTLSModeLabel,
2286
		},
2287
	}
2288
	for i := 0; i < b.N; i++ {
2289
		sd.WorkloadInstanceHandler(fi1, model.EventAdd)
2290
		sd.WorkloadInstanceHandler(fi2, model.EventAdd)
2291
		sd.WorkloadInstanceHandler(fi3, model.EventDelete)
2292

2293
		sd.WorkloadInstanceHandler(fi2, model.EventDelete)
2294
		sd.WorkloadInstanceHandler(fi1, model.EventDelete)
2295
		sd.WorkloadInstanceHandler(fi3, model.EventDelete)
2296
	}
2297
}
2298

2299
func BenchmarkWorkloadEntryHandler(b *testing.B) {
2300
	// Setup a couple workload entries for test. These will be selected by the `selector` SE
2301
	wle := createWorkloadEntry("wl", selector.Name,
2302
		&networking.WorkloadEntry{
2303
			Address:        "2.2.2.2",
2304
			Labels:         map[string]string{"app": "wle"},
2305
			ServiceAccount: "default",
2306
		})
2307
	wle2 := createWorkloadEntry("wl2", selector.Name,
2308
		&networking.WorkloadEntry{
2309
			Address:        "3.3.3.3",
2310
			Labels:         map[string]string{"app": "wle"},
2311
			ServiceAccount: "default",
2312
		})
2313
	dnsWle := createWorkloadEntry("dnswl", dnsSelector.Namespace,
2314
		&networking.WorkloadEntry{
2315
			Address:        "4.4.4.4",
2316
			Labels:         map[string]string{"app": "dns-wle"},
2317
			ServiceAccount: "default",
2318
		})
2319

2320
	store, sd := initServiceDiscoveryWithoutEvents(b)
2321
	stopCh := make(chan struct{})
2322
	go sd.Run(stopCh)
2323
	defer close(stopCh)
2324
	// Add just the ServiceEntry with selector. We should see no instances
2325
	createConfigs([]*config.Config{selector}, store, b)
2326

2327
	for i := 0; i < b.N; i++ {
2328
		sd.workloadEntryHandler(config.Config{}, *wle, model.EventAdd)
2329
		sd.workloadEntryHandler(config.Config{}, *dnsWle, model.EventAdd)
2330
		sd.workloadEntryHandler(config.Config{}, *wle2, model.EventAdd)
2331

2332
		sd.workloadEntryHandler(config.Config{}, *wle, model.EventDelete)
2333
		sd.workloadEntryHandler(config.Config{}, *dnsWle, model.EventDelete)
2334
		sd.workloadEntryHandler(config.Config{}, *wle2, model.EventDelete)
2335
	}
2336
}
2337

2338
func GetEndpoints(s *model.Service, endpoints *model.EndpointIndex) []*model.IstioEndpoint {
2339
	return GetEndpointsForPort(s, endpoints, 0)
2340
}
2341

2342
func GetEndpointsForPort(s *model.Service, endpoints *model.EndpointIndex, port int) []*model.IstioEndpoint {
2343
	shards, ok := endpoints.ShardsForService(string(s.Hostname), s.Attributes.Namespace)
2344
	if !ok {
2345
		return nil
2346
	}
2347
	var pn string
2348
	for _, p := range s.Ports {
2349
		if p.Port == port {
2350
			pn = p.Name
2351
			break
2352
		}
2353
	}
2354
	if pn == "" && port != 0 {
2355
		return nil
2356
	}
2357
	shards.RLock()
2358
	defer shards.RUnlock()
2359
	return slices.FilterInPlace(slices.Flatten(maps.Values(shards.Shards)), func(endpoint *model.IstioEndpoint) bool {
2360
		return pn == "" || endpoint.ServicePortName == pn
2361
	})
2362
}
2363

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

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

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

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