istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/serviceregistry/serviceentry/controller_test.go (about) 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 }