istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/serviceregistry/serviceentry/conversion_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  	"encoding/json"
    19  	"strings"
    20  	"testing"
    21  	"time"
    22  
    23  	"istio.io/api/label"
    24  	networking "istio.io/api/networking/v1alpha3"
    25  	"istio.io/istio/pilot/pkg/features"
    26  	"istio.io/istio/pilot/pkg/model"
    27  	"istio.io/istio/pilot/pkg/serviceregistry/provider"
    28  	labelutil "istio.io/istio/pilot/pkg/serviceregistry/util/label"
    29  	"istio.io/istio/pilot/test/util"
    30  	"istio.io/istio/pkg/cluster"
    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/protocol"
    36  	"istio.io/istio/pkg/config/schema/gvk"
    37  	"istio.io/istio/pkg/network"
    38  	"istio.io/istio/pkg/spiffe"
    39  	"istio.io/istio/pkg/test"
    40  )
    41  
    42  var (
    43  	GlobalTime = time.Now()
    44  	httpNone   = &config.Config{
    45  		Meta: config.Meta{
    46  			GroupVersionKind:  gvk.ServiceEntry,
    47  			Name:              "httpNone",
    48  			Namespace:         "httpNone",
    49  			Domain:            "svc.cluster.local",
    50  			CreationTimestamp: GlobalTime,
    51  		},
    52  		Spec: &networking.ServiceEntry{
    53  			Hosts: []string{"*.google.com"},
    54  			Ports: []*networking.ServicePort{
    55  				{Number: 80, Name: "http-number", Protocol: "http"},
    56  				{Number: 8080, Name: "http2-number", Protocol: "http2"},
    57  			},
    58  			Location:   networking.ServiceEntry_MESH_EXTERNAL,
    59  			Resolution: networking.ServiceEntry_NONE,
    60  		},
    61  	}
    62  )
    63  
    64  var tcpNone = &config.Config{
    65  	Meta: config.Meta{
    66  		GroupVersionKind:  gvk.ServiceEntry,
    67  		Name:              "tcpNone",
    68  		Namespace:         "tcpNone",
    69  		CreationTimestamp: GlobalTime,
    70  	},
    71  	Spec: &networking.ServiceEntry{
    72  		Hosts:     []string{"tcpnone.com"},
    73  		Addresses: []string{"172.217.0.0/16"},
    74  		Ports: []*networking.ServicePort{
    75  			{Number: 444, Name: "tcp-444", Protocol: "tcp"},
    76  		},
    77  		Location:   networking.ServiceEntry_MESH_EXTERNAL,
    78  		Resolution: networking.ServiceEntry_NONE,
    79  	},
    80  }
    81  
    82  var httpStatic = &config.Config{
    83  	Meta: config.Meta{
    84  		GroupVersionKind:  gvk.ServiceEntry,
    85  		Name:              "httpStatic",
    86  		Namespace:         "httpStatic",
    87  		CreationTimestamp: GlobalTime,
    88  	},
    89  	Spec: &networking.ServiceEntry{
    90  		Hosts: []string{"*.google.com"},
    91  		Ports: []*networking.ServicePort{
    92  			{Number: 80, Name: "http-port", Protocol: "http"},
    93  			{Number: 8080, Name: "http-alt-port", Protocol: "http"},
    94  		},
    95  		Endpoints: []*networking.WorkloadEntry{
    96  			{
    97  				Address: "2.2.2.2",
    98  				Ports:   map[string]uint32{"http-port": 7080, "http-alt-port": 18080},
    99  				Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   100  			},
   101  			{
   102  				Address: "3.3.3.3",
   103  				Ports:   map[string]uint32{"http-port": 1080},
   104  				Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   105  			},
   106  			{
   107  				Address: "4.4.4.4",
   108  				Ports:   map[string]uint32{"http-port": 1080},
   109  				Labels:  map[string]string{"foo": "bar"},
   110  			},
   111  		},
   112  		Location:   networking.ServiceEntry_MESH_EXTERNAL,
   113  		Resolution: networking.ServiceEntry_STATIC,
   114  	},
   115  }
   116  
   117  // Shares the same host as httpStatic, but adds some endpoints. We expect these to be merge
   118  var httpStaticOverlay = &config.Config{
   119  	Meta: config.Meta{
   120  		GroupVersionKind:  gvk.ServiceEntry,
   121  		Name:              "httpStaticOverlay",
   122  		Namespace:         "httpStatic",
   123  		CreationTimestamp: GlobalTime,
   124  	},
   125  	Spec: &networking.ServiceEntry{
   126  		Hosts: []string{"*.google.com"},
   127  		Ports: []*networking.ServicePort{
   128  			{Number: 4567, Name: "http-port", Protocol: "http"},
   129  		},
   130  		Endpoints: []*networking.WorkloadEntry{
   131  			{
   132  				Address: "5.5.5.5",
   133  				Labels:  map[string]string{"overlay": "bar"},
   134  			},
   135  		},
   136  		Location:   networking.ServiceEntry_MESH_EXTERNAL,
   137  		Resolution: networking.ServiceEntry_STATIC,
   138  	},
   139  }
   140  
   141  var httpDNSnoEndpoints = &config.Config{
   142  	Meta: config.Meta{
   143  		GroupVersionKind:  gvk.ServiceEntry,
   144  		Name:              "httpDNSnoEndpoints",
   145  		Namespace:         "httpDNSnoEndpoints",
   146  		CreationTimestamp: GlobalTime,
   147  	},
   148  	Spec: &networking.ServiceEntry{
   149  		Hosts: []string{"google.com", "www.wikipedia.org"},
   150  		Ports: []*networking.ServicePort{
   151  			{Number: 80, Name: "http-port", Protocol: "http"},
   152  			{Number: 8080, Name: "http-alt-port", Protocol: "http"},
   153  		},
   154  		Location:        networking.ServiceEntry_MESH_EXTERNAL,
   155  		Resolution:      networking.ServiceEntry_DNS,
   156  		SubjectAltNames: []string{"google.com"},
   157  	},
   158  }
   159  
   160  var httpDNSRRnoEndpoints = &config.Config{
   161  	Meta: config.Meta{
   162  		GroupVersionKind:  gvk.ServiceEntry,
   163  		Name:              "httpDNSRRnoEndpoints",
   164  		Namespace:         "httpDNSRRnoEndpoints",
   165  		CreationTimestamp: GlobalTime,
   166  	},
   167  	Spec: &networking.ServiceEntry{
   168  		Hosts: []string{"api.istio.io"},
   169  		Ports: []*networking.ServicePort{
   170  			{Number: 80, Name: "http-port", Protocol: "http"},
   171  			{Number: 8080, Name: "http-alt-port", Protocol: "http"},
   172  		},
   173  		Location:        networking.ServiceEntry_MESH_EXTERNAL,
   174  		Resolution:      networking.ServiceEntry_DNS_ROUND_ROBIN,
   175  		SubjectAltNames: []string{"api.istio.io"},
   176  	},
   177  }
   178  
   179  var dnsTargetPort = &config.Config{
   180  	Meta: config.Meta{
   181  		GroupVersionKind:  gvk.ServiceEntry,
   182  		Name:              "dnsTargetPort",
   183  		Namespace:         "dnsTargetPort",
   184  		CreationTimestamp: GlobalTime,
   185  	},
   186  	Spec: &networking.ServiceEntry{
   187  		Hosts: []string{"google.com"},
   188  		Ports: []*networking.ServicePort{
   189  			{Number: 80, Name: "http-port", Protocol: "http", TargetPort: 8080},
   190  		},
   191  		Location:   networking.ServiceEntry_MESH_EXTERNAL,
   192  		Resolution: networking.ServiceEntry_DNS,
   193  	},
   194  }
   195  
   196  var httpDNS = &config.Config{
   197  	Meta: config.Meta{
   198  		GroupVersionKind:  gvk.ServiceEntry,
   199  		Name:              "httpDNS",
   200  		Namespace:         "httpDNS",
   201  		CreationTimestamp: GlobalTime,
   202  	},
   203  	Spec: &networking.ServiceEntry{
   204  		Hosts: []string{"*.google.com"},
   205  		Ports: []*networking.ServicePort{
   206  			{Number: 80, Name: "http-port", Protocol: "http"},
   207  			{Number: 8080, Name: "http-alt-port", Protocol: "http"},
   208  		},
   209  		Endpoints: []*networking.WorkloadEntry{
   210  			{
   211  				Address: "us.google.com",
   212  				Ports:   map[string]uint32{"http-port": 7080, "http-alt-port": 18080},
   213  				Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   214  			},
   215  			{
   216  				Address: "uk.google.com",
   217  				Ports:   map[string]uint32{"http-port": 1080},
   218  				Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   219  			},
   220  			{
   221  				Address: "de.google.com",
   222  				Labels:  map[string]string{"foo": "bar", label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   223  			},
   224  		},
   225  		Location:   networking.ServiceEntry_MESH_EXTERNAL,
   226  		Resolution: networking.ServiceEntry_DNS,
   227  	},
   228  }
   229  
   230  var httpDNSRR = &config.Config{
   231  	Meta: config.Meta{
   232  		GroupVersionKind:  gvk.ServiceEntry,
   233  		Name:              "httpDNSRR",
   234  		Namespace:         "httpDNSRR",
   235  		CreationTimestamp: GlobalTime,
   236  	},
   237  	Spec: &networking.ServiceEntry{
   238  		Hosts: []string{"*.istio.io"},
   239  		Ports: []*networking.ServicePort{
   240  			{Number: 80, Name: "http-port", Protocol: "http"},
   241  			{Number: 8080, Name: "http-alt-port", Protocol: "http"},
   242  		},
   243  		Endpoints: []*networking.WorkloadEntry{
   244  			{
   245  				Address: "api-v1.istio.io",
   246  				Ports:   map[string]uint32{"http-port": 7080, "http-alt-port": 18080},
   247  				Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   248  			},
   249  		},
   250  		Location:   networking.ServiceEntry_MESH_EXTERNAL,
   251  		Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN,
   252  	},
   253  }
   254  
   255  var tcpDNS = &config.Config{
   256  	Meta: config.Meta{
   257  		GroupVersionKind:  gvk.ServiceEntry,
   258  		Name:              "tcpDNS",
   259  		Namespace:         "tcpDNS",
   260  		CreationTimestamp: GlobalTime,
   261  	},
   262  	Spec: &networking.ServiceEntry{
   263  		Hosts: []string{"tcpdns.com"},
   264  		Ports: []*networking.ServicePort{
   265  			{Number: 444, Name: "tcp-444", Protocol: "tcp"},
   266  		},
   267  		Endpoints: []*networking.WorkloadEntry{
   268  			{
   269  				Address: "lon.google.com",
   270  				Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   271  			},
   272  			{
   273  				Address: "in.google.com",
   274  				Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   275  			},
   276  		},
   277  		Location:   networking.ServiceEntry_MESH_EXTERNAL,
   278  		Resolution: networking.ServiceEntry_DNS,
   279  	},
   280  }
   281  
   282  var tcpStatic = &config.Config{
   283  	Meta: config.Meta{
   284  		GroupVersionKind:  gvk.ServiceEntry,
   285  		Name:              "tcpStatic",
   286  		Namespace:         "tcpStatic",
   287  		CreationTimestamp: GlobalTime,
   288  	},
   289  	Spec: &networking.ServiceEntry{
   290  		Hosts:     []string{"tcpstatic.com"},
   291  		Addresses: []string{"172.217.0.1"},
   292  		Ports: []*networking.ServicePort{
   293  			{Number: 444, Name: "tcp-444", Protocol: "tcp"},
   294  		},
   295  		Endpoints: []*networking.WorkloadEntry{
   296  			{
   297  				Address: "1.1.1.1",
   298  				Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   299  			},
   300  			{
   301  				Address: "2.2.2.2",
   302  				Labels:  map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   303  			},
   304  		},
   305  		Location:   networking.ServiceEntry_MESH_EXTERNAL,
   306  		Resolution: networking.ServiceEntry_STATIC,
   307  	},
   308  }
   309  
   310  var httpNoneInternal = &config.Config{
   311  	Meta: config.Meta{
   312  		GroupVersionKind:  gvk.ServiceEntry,
   313  		Name:              "httpNoneInternal",
   314  		Namespace:         "httpNoneInternal",
   315  		CreationTimestamp: GlobalTime,
   316  	},
   317  	Spec: &networking.ServiceEntry{
   318  		Hosts: []string{"*.google.com"},
   319  		Ports: []*networking.ServicePort{
   320  			{Number: 80, Name: "http-number", Protocol: "http"},
   321  			{Number: 8080, Name: "http2-number", Protocol: "http2"},
   322  		},
   323  		Location:   networking.ServiceEntry_MESH_INTERNAL,
   324  		Resolution: networking.ServiceEntry_NONE,
   325  	},
   326  }
   327  
   328  var tcpNoneInternal = &config.Config{
   329  	Meta: config.Meta{
   330  		GroupVersionKind:  gvk.ServiceEntry,
   331  		Name:              "tcpNoneInternal",
   332  		Namespace:         "tcpNoneInternal",
   333  		CreationTimestamp: GlobalTime,
   334  	},
   335  	Spec: &networking.ServiceEntry{
   336  		Hosts:     []string{"tcpinternal.com"},
   337  		Addresses: []string{"172.217.0.0/16"},
   338  		Ports: []*networking.ServicePort{
   339  			{Number: 444, Name: "tcp-444", Protocol: "tcp"},
   340  		},
   341  		Location:   networking.ServiceEntry_MESH_INTERNAL,
   342  		Resolution: networking.ServiceEntry_NONE,
   343  	},
   344  }
   345  
   346  var multiAddrInternal = &config.Config{
   347  	Meta: config.Meta{
   348  		GroupVersionKind:  gvk.ServiceEntry,
   349  		Name:              "multiAddrInternal",
   350  		Namespace:         "multiAddrInternal",
   351  		CreationTimestamp: GlobalTime,
   352  	},
   353  	Spec: &networking.ServiceEntry{
   354  		Hosts:     []string{"tcp1.com", "tcp2.com"},
   355  		Addresses: []string{"1.1.1.0/16", "2.2.2.0/16"},
   356  		Ports: []*networking.ServicePort{
   357  			{Number: 444, Name: "tcp-444", Protocol: "tcp"},
   358  		},
   359  		Location:   networking.ServiceEntry_MESH_INTERNAL,
   360  		Resolution: networking.ServiceEntry_NONE,
   361  	},
   362  }
   363  
   364  var udsLocal = &config.Config{
   365  	Meta: config.Meta{
   366  		GroupVersionKind:  gvk.ServiceEntry,
   367  		Name:              "udsLocal",
   368  		Namespace:         "udsLocal",
   369  		CreationTimestamp: GlobalTime,
   370  		Labels:            map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   371  	},
   372  	Spec: &networking.ServiceEntry{
   373  		Hosts: []string{"uds.cluster.local"},
   374  		Ports: []*networking.ServicePort{
   375  			{Number: 6553, Name: "grpc-1", Protocol: "grpc"},
   376  		},
   377  		Endpoints: []*networking.WorkloadEntry{
   378  			{Address: "unix:///test/sock", Labels: map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel}},
   379  		},
   380  		Resolution: networking.ServiceEntry_STATIC,
   381  	},
   382  }
   383  
   384  // ServiceEntry DNS with a selector
   385  var selectorDNS = &config.Config{
   386  	Meta: config.Meta{
   387  		GroupVersionKind:  gvk.ServiceEntry,
   388  		Name:              "selector",
   389  		Namespace:         "selector",
   390  		CreationTimestamp: GlobalTime,
   391  	},
   392  	Spec: &networking.ServiceEntry{
   393  		Hosts: []string{"selector.com"},
   394  		Ports: []*networking.ServicePort{
   395  			{Number: 444, Name: "tcp-444", Protocol: "tcp"},
   396  			{Number: 445, Name: "http-445", Protocol: "http"},
   397  		},
   398  		WorkloadSelector: &networking.WorkloadSelector{
   399  			Labels: map[string]string{"app": "wle"},
   400  		},
   401  		Resolution: networking.ServiceEntry_DNS,
   402  	},
   403  }
   404  
   405  // ServiceEntry with a selector
   406  var selector = &config.Config{
   407  	Meta: config.Meta{
   408  		GroupVersionKind:  gvk.ServiceEntry,
   409  		Name:              "selector",
   410  		Namespace:         "selector",
   411  		CreationTimestamp: GlobalTime,
   412  	},
   413  	Spec: &networking.ServiceEntry{
   414  		Hosts: []string{"selector.com"},
   415  		Ports: []*networking.ServicePort{
   416  			{Number: 444, Name: "tcp-444", Protocol: "tcp"},
   417  			{Number: 445, Name: "http-445", Protocol: "http"},
   418  		},
   419  		WorkloadSelector: &networking.WorkloadSelector{
   420  			Labels: map[string]string{"app": "wle"},
   421  		},
   422  		Resolution: networking.ServiceEntry_STATIC,
   423  	},
   424  }
   425  
   426  // DNS ServiceEntry with a selector
   427  var dnsSelector = &config.Config{
   428  	Meta: config.Meta{
   429  		GroupVersionKind:  gvk.ServiceEntry,
   430  		Name:              "dns-selector",
   431  		Namespace:         "dns-selector",
   432  		CreationTimestamp: GlobalTime,
   433  		Labels:            map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   434  	},
   435  	Spec: &networking.ServiceEntry{
   436  		Hosts: []string{"dns.selector.com"},
   437  		Ports: []*networking.ServicePort{
   438  			{Number: 444, Name: "tcp-444", Protocol: "tcp"},
   439  			{Number: 445, Name: "http-445", Protocol: "http"},
   440  		},
   441  		WorkloadSelector: &networking.WorkloadSelector{
   442  			Labels: map[string]string{"app": "dns-wle"},
   443  		},
   444  		Resolution: networking.ServiceEntry_DNS,
   445  	},
   446  }
   447  
   448  // Service Entry with DNSRoundRobinLB
   449  var dnsRoundRobinLBSE1 = &config.Config{
   450  	Meta: config.Meta{
   451  		GroupVersionKind:  gvk.ServiceEntry,
   452  		Name:              "dns-round-robin-1",
   453  		Namespace:         "dns",
   454  		CreationTimestamp: GlobalTime,
   455  		Labels:            map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   456  	},
   457  	Spec: &networking.ServiceEntry{
   458  		Hosts: []string{"example.com"},
   459  		Ports: []*networking.ServicePort{
   460  			{Number: 445, Name: "http-445", Protocol: "http"},
   461  			{Number: 446, Name: "http-446", Protocol: "http"},
   462  		},
   463  		Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN,
   464  	},
   465  }
   466  
   467  var dnsRoundRobinLBSE2 = &config.Config{
   468  	Meta: config.Meta{
   469  		GroupVersionKind:  gvk.ServiceEntry,
   470  		Name:              "dns-round-robin-2",
   471  		Namespace:         "dns",
   472  		CreationTimestamp: GlobalTime,
   473  		Labels:            map[string]string{label.SecurityTlsMode.Name: model.IstioMutualTLSModeLabel},
   474  	},
   475  	Spec: &networking.ServiceEntry{
   476  		Hosts: []string{"example.com"},
   477  		Ports: []*networking.ServicePort{
   478  			{Number: 445, Name: "http-445", Protocol: "http"},
   479  		},
   480  		Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN,
   481  	},
   482  }
   483  
   484  func createWorkloadEntry(name, namespace string, spec *networking.WorkloadEntry) *config.Config {
   485  	return &config.Config{
   486  		Meta: config.Meta{
   487  			GroupVersionKind:  gvk.WorkloadEntry,
   488  			Name:              name,
   489  			Namespace:         namespace,
   490  			CreationTimestamp: GlobalTime,
   491  		},
   492  		Spec: spec,
   493  	}
   494  }
   495  
   496  func convertPortNameToProtocol(name string) protocol.Instance {
   497  	prefix := name
   498  	i := strings.Index(name, "-")
   499  	if i >= 0 {
   500  		prefix = name[:i]
   501  	}
   502  	return protocol.Parse(prefix)
   503  }
   504  
   505  func makeService(hostname host.Name, configNamespace, address string, ports map[string]int,
   506  	external bool, resolution model.Resolution, serviceAccounts ...string,
   507  ) *model.Service {
   508  	svc := &model.Service{
   509  		CreationTime:    GlobalTime,
   510  		Hostname:        hostname,
   511  		DefaultAddress:  address,
   512  		MeshExternal:    external,
   513  		Resolution:      resolution,
   514  		ServiceAccounts: serviceAccounts,
   515  		Attributes: model.ServiceAttributes{
   516  			ServiceRegistry: provider.External,
   517  			Name:            string(hostname),
   518  			Namespace:       configNamespace,
   519  		},
   520  	}
   521  
   522  	if external && features.CanonicalServiceForMeshExternalServiceEntry {
   523  		if svc.Attributes.Labels == nil {
   524  			svc.Attributes.Labels = make(map[string]string)
   525  		}
   526  		svc.Attributes.Labels["service.istio.io/canonical-name"] = configNamespace
   527  		svc.Attributes.Labels["service.istio.io/canonical-revision"] = "latest"
   528  	}
   529  
   530  	svcPorts := make(model.PortList, 0, len(ports))
   531  	for name, port := range ports {
   532  		svcPort := &model.Port{
   533  			Name:     name,
   534  			Port:     port,
   535  			Protocol: convertPortNameToProtocol(name),
   536  		}
   537  		svcPorts = append(svcPorts, svcPort)
   538  	}
   539  
   540  	sortPorts(svcPorts)
   541  	svc.Ports = svcPorts
   542  	return svc
   543  }
   544  
   545  // MTLSMode is the expected instance mtls settings. This is for test setup only
   546  type MTLSMode int
   547  
   548  const (
   549  	MTLS = iota
   550  	// Like mTLS, but no label is defined on the endpoint
   551  	MTLSUnlabelled
   552  	PlainText
   553  )
   554  
   555  // nolint: unparam
   556  func makeInstanceWithServiceAccount(cfg *config.Config, address string, port int,
   557  	svcPort *networking.ServicePort, svcLabels map[string]string, serviceAccount string,
   558  ) *model.ServiceInstance {
   559  	i := makeInstance(cfg, address, port, svcPort, svcLabels, MTLSUnlabelled)
   560  	i.Endpoint.ServiceAccount = spiffe.MustGenSpiffeURI(i.Service.Attributes.Namespace, serviceAccount)
   561  	return i
   562  }
   563  
   564  // nolint: unparam
   565  func makeTarget(cfg *config.Config, address string, port int,
   566  	svcPort *networking.ServicePort, svcLabels map[string]string, mtlsMode MTLSMode,
   567  ) model.ServiceTarget {
   568  	services := convertServices(*cfg)
   569  	svc := services[0] // default
   570  	for _, s := range services {
   571  		if string(s.Hostname) == address {
   572  			svc = s
   573  			break
   574  		}
   575  	}
   576  	if mtlsMode == MTLS {
   577  		if svcLabels == nil {
   578  			svcLabels = map[string]string{}
   579  		}
   580  		svcLabels[label.SecurityTlsMode.Name] = model.IstioMutualTLSModeLabel
   581  	}
   582  	return model.ServiceTarget{
   583  		Service: svc,
   584  		Port: model.ServiceInstancePort{
   585  			ServicePort: &model.Port{
   586  				Name:     svcPort.Name,
   587  				Port:     int(svcPort.Number),
   588  				Protocol: protocol.Parse(svcPort.Protocol),
   589  			},
   590  			TargetPort: uint32(port),
   591  		},
   592  	}
   593  }
   594  
   595  // nolint: unparam
   596  func makeInstance(cfg *config.Config, address string, port int,
   597  	svcPort *networking.ServicePort, svcLabels map[string]string, mtlsMode MTLSMode,
   598  ) *model.ServiceInstance {
   599  	services := convertServices(*cfg)
   600  	svc := services[0] // default
   601  	for _, s := range services {
   602  		if string(s.Hostname) == address {
   603  			svc = s
   604  			break
   605  		}
   606  	}
   607  	tlsMode := model.DisabledTLSModeLabel
   608  	if mtlsMode == MTLS || mtlsMode == MTLSUnlabelled {
   609  		tlsMode = model.IstioMutualTLSModeLabel
   610  	}
   611  	if mtlsMode == MTLS {
   612  		if svcLabels == nil {
   613  			svcLabels = map[string]string{}
   614  		}
   615  		svcLabels[label.SecurityTlsMode.Name] = model.IstioMutualTLSModeLabel
   616  	}
   617  	return &model.ServiceInstance{
   618  		Service: svc,
   619  		Endpoint: &model.IstioEndpoint{
   620  			Address:              address,
   621  			EndpointPort:         uint32(port),
   622  			ServicePortName:      svcPort.Name,
   623  			LegacyClusterPortKey: int(svcPort.Number),
   624  			Labels:               svcLabels,
   625  			TLSMode:              tlsMode,
   626  		},
   627  		ServicePort: &model.Port{
   628  			Name:     svcPort.Name,
   629  			Port:     int(svcPort.Number),
   630  			Protocol: protocol.Parse(svcPort.Protocol),
   631  		},
   632  	}
   633  }
   634  
   635  func TestConvertService(t *testing.T) {
   636  	testConvertServiceBody(t)
   637  	test.SetForTest(t, &features.CanonicalServiceForMeshExternalServiceEntry, true)
   638  	testConvertServiceBody(t)
   639  }
   640  
   641  func testConvertServiceBody(t *testing.T) {
   642  	t.Helper()
   643  
   644  	serviceTests := []struct {
   645  		externalSvc *config.Config
   646  		services    []*model.Service
   647  	}{
   648  		{
   649  			// service entry http
   650  			externalSvc: httpNone,
   651  			services: []*model.Service{
   652  				makeService("*.google.com", "httpNone", constants.UnspecifiedIP,
   653  					map[string]int{"http-number": 80, "http2-number": 8080}, true, model.Passthrough),
   654  			},
   655  		},
   656  		{
   657  			// service entry tcp
   658  			externalSvc: tcpNone,
   659  			services: []*model.Service{
   660  				makeService("tcpnone.com", "tcpNone", "172.217.0.0/16",
   661  					map[string]int{"tcp-444": 444}, true, model.Passthrough),
   662  			},
   663  		},
   664  		{
   665  			// service entry http  static
   666  			externalSvc: httpStatic,
   667  			services: []*model.Service{
   668  				makeService("*.google.com", "httpStatic", constants.UnspecifiedIP,
   669  					map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.ClientSideLB),
   670  			},
   671  		},
   672  		{
   673  			// service entry DNS with no endpoints
   674  			externalSvc: httpDNSnoEndpoints,
   675  			services: []*model.Service{
   676  				makeService("google.com", "httpDNSnoEndpoints", constants.UnspecifiedIP,
   677  					map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSLB, "google.com"),
   678  				makeService("www.wikipedia.org", "httpDNSnoEndpoints", constants.UnspecifiedIP,
   679  					map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSLB, "google.com"),
   680  			},
   681  		},
   682  		{
   683  			// service entry dns
   684  			externalSvc: httpDNS,
   685  			services: []*model.Service{
   686  				makeService("*.google.com", "httpDNS", constants.UnspecifiedIP,
   687  					map[string]int{"http-port": 80, "http-alt-port": 8080}, true, model.DNSLB),
   688  			},
   689  		},
   690  		{
   691  			// service entry dns with target port
   692  			externalSvc: dnsTargetPort,
   693  			services: []*model.Service{
   694  				makeService("google.com", "dnsTargetPort", constants.UnspecifiedIP,
   695  					map[string]int{"http-port": 80}, true, model.DNSLB),
   696  			},
   697  		},
   698  		{
   699  			// service entry tcp DNS
   700  			externalSvc: tcpDNS,
   701  			services: []*model.Service{
   702  				makeService("tcpdns.com", "tcpDNS", constants.UnspecifiedIP,
   703  					map[string]int{"tcp-444": 444}, true, model.DNSLB),
   704  			},
   705  		},
   706  		{
   707  			// service entry tcp static
   708  			externalSvc: tcpStatic,
   709  			services: []*model.Service{
   710  				makeService("tcpstatic.com", "tcpStatic", "172.217.0.1",
   711  					map[string]int{"tcp-444": 444}, true, model.ClientSideLB),
   712  			},
   713  		},
   714  		{
   715  			// service entry http internal
   716  			externalSvc: httpNoneInternal,
   717  			services: []*model.Service{
   718  				makeService("*.google.com", "httpNoneInternal", constants.UnspecifiedIP,
   719  					map[string]int{"http-number": 80, "http2-number": 8080}, false, model.Passthrough),
   720  			},
   721  		},
   722  		{
   723  			// service entry tcp internal
   724  			externalSvc: tcpNoneInternal,
   725  			services: []*model.Service{
   726  				makeService("tcpinternal.com", "tcpNoneInternal", "172.217.0.0/16",
   727  					map[string]int{"tcp-444": 444}, false, model.Passthrough),
   728  			},
   729  		},
   730  		{
   731  			// service entry multiAddrInternal
   732  			externalSvc: multiAddrInternal,
   733  			services: []*model.Service{
   734  				makeService("tcp1.com", "multiAddrInternal", "1.1.1.0/16",
   735  					map[string]int{"tcp-444": 444}, false, model.Passthrough),
   736  				makeService("tcp1.com", "multiAddrInternal", "2.2.2.0/16",
   737  					map[string]int{"tcp-444": 444}, false, model.Passthrough),
   738  				makeService("tcp2.com", "multiAddrInternal", "1.1.1.0/16",
   739  					map[string]int{"tcp-444": 444}, false, model.Passthrough),
   740  				makeService("tcp2.com", "multiAddrInternal", "2.2.2.0/16",
   741  					map[string]int{"tcp-444": 444}, false, model.Passthrough),
   742  			},
   743  		},
   744  	}
   745  
   746  	selectorSvc := makeService("selector.com", "selector", "0.0.0.0",
   747  		map[string]int{"tcp-444": 444, "http-445": 445}, true, model.ClientSideLB)
   748  	selectorSvc.Attributes.LabelSelectors = map[string]string{"app": "wle"}
   749  
   750  	serviceTests = append(serviceTests, struct {
   751  		externalSvc *config.Config
   752  		services    []*model.Service
   753  	}{
   754  		externalSvc: selector,
   755  		services:    []*model.Service{selectorSvc},
   756  	})
   757  
   758  	for _, tt := range serviceTests {
   759  		services := convertServices(*tt.externalSvc)
   760  		if err := compare(t, services, tt.services); err != nil {
   761  			t.Errorf("testcase: %v\n%v ", tt.externalSvc.Name, err)
   762  		}
   763  	}
   764  }
   765  
   766  func TestConvertInstances(t *testing.T) {
   767  	serviceInstanceTests := []struct {
   768  		externalSvc *config.Config
   769  		out         []*model.ServiceInstance
   770  	}{
   771  		{
   772  			// single instance with multiple ports
   773  			externalSvc: httpNone,
   774  			// DNS type none means service should not have a registered instance
   775  			out: []*model.ServiceInstance{},
   776  		},
   777  		{
   778  			// service entry tcp
   779  			externalSvc: tcpNone,
   780  			// DNS type none means service should not have a registered instance
   781  			out: []*model.ServiceInstance{},
   782  		},
   783  		{
   784  			// service entry static
   785  			externalSvc: httpStatic,
   786  			out: []*model.ServiceInstance{
   787  				makeInstance(httpStatic, "2.2.2.2", 7080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
   788  				makeInstance(httpStatic, "2.2.2.2", 18080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS),
   789  				makeInstance(httpStatic, "3.3.3.3", 1080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
   790  				makeInstance(httpStatic, "3.3.3.3", 8080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS),
   791  				makeInstance(httpStatic, "4.4.4.4", 1080, httpStatic.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, PlainText),
   792  				makeInstance(httpStatic, "4.4.4.4", 8080, httpStatic.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"foo": "bar"}, PlainText),
   793  			},
   794  		},
   795  		{
   796  			// service entry DNS with no endpoints
   797  			externalSvc: httpDNSnoEndpoints,
   798  			out: []*model.ServiceInstance{
   799  				makeInstance(httpDNSnoEndpoints, "google.com", 80, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText),
   800  				makeInstance(httpDNSnoEndpoints, "google.com", 8080, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[1], nil, PlainText),
   801  				makeInstance(httpDNSnoEndpoints, "www.wikipedia.org", 80, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText),
   802  				makeInstance(httpDNSnoEndpoints, "www.wikipedia.org", 8080, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[1], nil, PlainText),
   803  			},
   804  		},
   805  		{
   806  			// service entry DNS with no endpoints using round robin
   807  			externalSvc: httpDNSRRnoEndpoints,
   808  			out: []*model.ServiceInstance{
   809  				makeInstance(httpDNSRRnoEndpoints, "api.istio.io", 80, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText),
   810  				makeInstance(httpDNSRRnoEndpoints, "api.istio.io", 8080, httpDNSnoEndpoints.Spec.(*networking.ServiceEntry).Ports[1], nil, PlainText),
   811  			},
   812  		},
   813  		{
   814  			// service entry DNS with workload selector and no endpoints
   815  			externalSvc: selectorDNS,
   816  			out:         []*model.ServiceInstance{},
   817  		},
   818  		{
   819  			// service entry dns
   820  			externalSvc: httpDNS,
   821  			out: []*model.ServiceInstance{
   822  				makeInstance(httpDNS, "us.google.com", 7080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
   823  				makeInstance(httpDNS, "us.google.com", 18080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS),
   824  				makeInstance(httpDNS, "uk.google.com", 1080, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
   825  				makeInstance(httpDNS, "uk.google.com", 8080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], nil, MTLS),
   826  				makeInstance(httpDNS, "de.google.com", 80, httpDNS.Spec.(*networking.ServiceEntry).Ports[0], map[string]string{"foo": "bar"}, MTLS),
   827  				makeInstance(httpDNS, "de.google.com", 8080, httpDNS.Spec.(*networking.ServiceEntry).Ports[1], map[string]string{"foo": "bar"}, MTLS),
   828  			},
   829  		},
   830  		{
   831  			// service entry dns with target port
   832  			externalSvc: dnsTargetPort,
   833  			out: []*model.ServiceInstance{
   834  				makeInstance(dnsTargetPort, "google.com", 8080, dnsTargetPort.Spec.(*networking.ServiceEntry).Ports[0], nil, PlainText),
   835  			},
   836  		},
   837  		{
   838  			// service entry tcp DNS
   839  			externalSvc: tcpDNS,
   840  			out: []*model.ServiceInstance{
   841  				makeInstance(tcpDNS, "lon.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
   842  				makeInstance(tcpDNS, "in.google.com", 444, tcpDNS.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
   843  			},
   844  		},
   845  		{
   846  			// service entry tcp static
   847  			externalSvc: tcpStatic,
   848  			out: []*model.ServiceInstance{
   849  				makeInstance(tcpStatic, "1.1.1.1", 444, tcpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
   850  				makeInstance(tcpStatic, "2.2.2.2", 444, tcpStatic.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
   851  			},
   852  		},
   853  		{
   854  			// service entry unix domain socket static
   855  			externalSvc: udsLocal,
   856  			out: []*model.ServiceInstance{
   857  				makeInstance(udsLocal, "/test/sock", 0, udsLocal.Spec.(*networking.ServiceEntry).Ports[0], nil, MTLS),
   858  			},
   859  		},
   860  	}
   861  
   862  	for _, tt := range serviceInstanceTests {
   863  		t.Run(strings.Join(tt.externalSvc.Spec.(*networking.ServiceEntry).Hosts, "_"), func(t *testing.T) {
   864  			s := &Controller{}
   865  			instances := s.convertServiceEntryToInstances(*tt.externalSvc, nil)
   866  			sortServiceInstances(instances)
   867  			sortServiceInstances(tt.out)
   868  			if err := compare(t, instances, tt.out); err != nil {
   869  				t.Fatalf("testcase: %v\n%v", tt.externalSvc.Name, err)
   870  			}
   871  		})
   872  	}
   873  }
   874  
   875  func TestConvertWorkloadEntryToServiceInstances(t *testing.T) {
   876  	labels := map[string]string{
   877  		"app": "wle",
   878  	}
   879  	serviceInstanceTests := []struct {
   880  		name      string
   881  		wle       *networking.WorkloadEntry
   882  		se        *config.Config
   883  		clusterID cluster.ID
   884  		out       []*model.ServiceInstance
   885  	}{
   886  		{
   887  			name: "simple",
   888  			wle: &networking.WorkloadEntry{
   889  				Address: "1.1.1.1",
   890  				Labels:  labels,
   891  			},
   892  			se: selector,
   893  			out: []*model.ServiceInstance{
   894  				makeInstance(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], labels, PlainText),
   895  				makeInstance(selector, "1.1.1.1", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], labels, PlainText),
   896  			},
   897  		},
   898  		{
   899  			name: "mtls",
   900  			wle: &networking.WorkloadEntry{
   901  				Address:        "1.1.1.1",
   902  				Labels:         labels,
   903  				ServiceAccount: "default",
   904  			},
   905  			se: selector,
   906  			out: []*model.ServiceInstance{
   907  				makeInstanceWithServiceAccount(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], labels, "default"),
   908  				makeInstanceWithServiceAccount(selector, "1.1.1.1", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], labels, "default"),
   909  			},
   910  		},
   911  		{
   912  			name: "replace-port",
   913  			wle: &networking.WorkloadEntry{
   914  				Address: "1.1.1.1",
   915  				Labels:  labels,
   916  				Ports: map[string]uint32{
   917  					"http-445": 8080,
   918  				},
   919  			},
   920  			se: selector,
   921  			out: []*model.ServiceInstance{
   922  				makeInstance(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], labels, PlainText),
   923  				makeInstance(selector, "1.1.1.1", 8080, selector.Spec.(*networking.ServiceEntry).Ports[1], labels, PlainText),
   924  			},
   925  		},
   926  		{
   927  			name: "augment label",
   928  			wle: &networking.WorkloadEntry{
   929  				Address:        "1.1.1.1",
   930  				Labels:         labels,
   931  				Locality:       "region1/zone1/sunzone1",
   932  				Network:        "network1",
   933  				ServiceAccount: "default",
   934  			},
   935  			se:        selector,
   936  			clusterID: "fakeCluster",
   937  			out: []*model.ServiceInstance{
   938  				makeInstanceWithServiceAccount(selector, "1.1.1.1", 444, selector.Spec.(*networking.ServiceEntry).Ports[0], labels, "default"),
   939  				makeInstanceWithServiceAccount(selector, "1.1.1.1", 445, selector.Spec.(*networking.ServiceEntry).Ports[1], labels, "default"),
   940  			},
   941  		},
   942  	}
   943  
   944  	for _, tt := range serviceInstanceTests {
   945  		t.Run(tt.name, func(t *testing.T) {
   946  			services := convertServices(*tt.se)
   947  			s := &Controller{}
   948  			instances := s.convertWorkloadEntryToServiceInstances(tt.wle, services, tt.se.Spec.(*networking.ServiceEntry), &configKey{}, tt.clusterID)
   949  			sortServiceInstances(instances)
   950  			sortServiceInstances(tt.out)
   951  
   952  			if tt.wle.Locality != "" || tt.clusterID != "" || tt.wle.Network != "" {
   953  				for _, serviceInstance := range tt.out {
   954  					serviceInstance.Endpoint.Locality = model.Locality{
   955  						Label:     tt.wle.Locality,
   956  						ClusterID: tt.clusterID,
   957  					}
   958  					serviceInstance.Endpoint.Network = network.ID(tt.wle.Network)
   959  					serviceInstance.Endpoint.Labels = labelutil.AugmentLabels(serviceInstance.Endpoint.Labels, tt.clusterID, tt.wle.Locality, "", network.ID(tt.wle.Network))
   960  				}
   961  			}
   962  
   963  			if err := compare(t, instances, tt.out); err != nil {
   964  				t.Fatal(err)
   965  			}
   966  		})
   967  	}
   968  }
   969  
   970  func TestConvertWorkloadEntryToWorkloadInstance(t *testing.T) {
   971  	workloadLabel := map[string]string{
   972  		"app": "wle",
   973  	}
   974  
   975  	clusterID := "fakeCluster"
   976  	expectedLabel := map[string]string{
   977  		"app":                       "wle",
   978  		"topology.istio.io/cluster": clusterID,
   979  	}
   980  
   981  	workloadInstanceTests := []struct {
   982  		name           string
   983  		getNetworkIDCb func(IP string, labels labels.Instance) network.ID
   984  		wle            config.Config
   985  		out            *model.WorkloadInstance
   986  	}{
   987  		{
   988  			name: "simple",
   989  			wle: config.Config{
   990  				Meta: config.Meta{
   991  					Namespace: "ns1",
   992  				},
   993  				Spec: &networking.WorkloadEntry{
   994  					Address: "1.1.1.1",
   995  					Labels:  workloadLabel,
   996  					Ports: map[string]uint32{
   997  						"http": 80,
   998  					},
   999  					ServiceAccount: "scooby",
  1000  				},
  1001  			},
  1002  			out: &model.WorkloadInstance{
  1003  				Namespace: "ns1",
  1004  				Kind:      model.WorkloadEntryKind,
  1005  				Endpoint: &model.IstioEndpoint{
  1006  					Labels:         expectedLabel,
  1007  					Address:        "1.1.1.1",
  1008  					ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby",
  1009  					TLSMode:        "istio",
  1010  					Namespace:      "ns1",
  1011  					Locality: model.Locality{
  1012  						ClusterID: cluster.ID(clusterID),
  1013  					},
  1014  				},
  1015  				PortMap: map[string]uint32{
  1016  					"http": 80,
  1017  				},
  1018  			},
  1019  		},
  1020  		{
  1021  			name: "simple - tls mode disabled",
  1022  			wle: config.Config{
  1023  				Meta: config.Meta{
  1024  					Namespace: "ns1",
  1025  				},
  1026  				Spec: &networking.WorkloadEntry{
  1027  					Address: "1.1.1.1",
  1028  					Labels: map[string]string{
  1029  						"security.istio.io/tlsMode": "disabled",
  1030  					},
  1031  					Ports: map[string]uint32{
  1032  						"http": 80,
  1033  					},
  1034  					ServiceAccount: "scooby",
  1035  				},
  1036  			},
  1037  			out: &model.WorkloadInstance{
  1038  				Namespace: "ns1",
  1039  				Kind:      model.WorkloadEntryKind,
  1040  				Endpoint: &model.IstioEndpoint{
  1041  					Labels: map[string]string{
  1042  						"security.istio.io/tlsMode": "disabled",
  1043  						"topology.istio.io/cluster": clusterID,
  1044  					},
  1045  					Address:        "1.1.1.1",
  1046  					ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby",
  1047  					TLSMode:        "disabled",
  1048  					Namespace:      "ns1",
  1049  					Locality: model.Locality{
  1050  						ClusterID: cluster.ID(clusterID),
  1051  					},
  1052  				},
  1053  				PortMap: map[string]uint32{
  1054  					"http": 80,
  1055  				},
  1056  			},
  1057  		},
  1058  		{
  1059  			name: "unix domain socket",
  1060  			wle: config.Config{
  1061  				Meta: config.Meta{
  1062  					Namespace: "ns1",
  1063  				},
  1064  				Spec: &networking.WorkloadEntry{
  1065  					Address:        "unix://foo/bar",
  1066  					ServiceAccount: "scooby",
  1067  				},
  1068  			},
  1069  			out: &model.WorkloadInstance{
  1070  				Namespace: "ns1",
  1071  				Kind:      model.WorkloadEntryKind,
  1072  				Endpoint: &model.IstioEndpoint{
  1073  					Labels: map[string]string{
  1074  						"topology.istio.io/cluster": clusterID,
  1075  					},
  1076  					Address:        "unix://foo/bar",
  1077  					ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby",
  1078  					TLSMode:        "istio",
  1079  					Namespace:      "ns1",
  1080  					Locality: model.Locality{
  1081  						ClusterID: cluster.ID(clusterID),
  1082  					},
  1083  				},
  1084  				DNSServiceEntryOnly: true,
  1085  			},
  1086  		},
  1087  		{
  1088  			name: "DNS address",
  1089  			wle: config.Config{
  1090  				Meta: config.Meta{
  1091  					Namespace: "ns1",
  1092  				},
  1093  				Spec: &networking.WorkloadEntry{
  1094  					Address:        "scooby.com",
  1095  					ServiceAccount: "scooby",
  1096  				},
  1097  			},
  1098  			out: &model.WorkloadInstance{
  1099  				Namespace: "ns1",
  1100  				Kind:      model.WorkloadEntryKind,
  1101  				Endpoint: &model.IstioEndpoint{
  1102  					Labels: map[string]string{
  1103  						"topology.istio.io/cluster": clusterID,
  1104  					},
  1105  					Address:        "scooby.com",
  1106  					ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby",
  1107  					TLSMode:        "istio",
  1108  					Namespace:      "ns1",
  1109  					Locality: model.Locality{
  1110  						ClusterID: cluster.ID(clusterID),
  1111  					},
  1112  				},
  1113  				DNSServiceEntryOnly: true,
  1114  			},
  1115  		},
  1116  		{
  1117  			name: "metadata labels only",
  1118  			wle: config.Config{
  1119  				Meta: config.Meta{
  1120  					Labels:    workloadLabel,
  1121  					Namespace: "ns1",
  1122  				},
  1123  				Spec: &networking.WorkloadEntry{
  1124  					Address: "1.1.1.1",
  1125  					Ports: map[string]uint32{
  1126  						"http": 80,
  1127  					},
  1128  					ServiceAccount: "scooby",
  1129  				},
  1130  			},
  1131  			out: &model.WorkloadInstance{
  1132  				Namespace: "ns1",
  1133  				Kind:      model.WorkloadEntryKind,
  1134  				Endpoint: &model.IstioEndpoint{
  1135  					Labels:         expectedLabel,
  1136  					Address:        "1.1.1.1",
  1137  					ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby",
  1138  					TLSMode:        "istio",
  1139  					Namespace:      "ns1",
  1140  					Locality: model.Locality{
  1141  						ClusterID: cluster.ID(clusterID),
  1142  					},
  1143  				},
  1144  				PortMap: map[string]uint32{
  1145  					"http": 80,
  1146  				},
  1147  			},
  1148  		},
  1149  		{
  1150  			name: "labels merge",
  1151  			wle: config.Config{
  1152  				Meta: config.Meta{
  1153  					Namespace: "ns1",
  1154  					Labels: map[string]string{
  1155  						"my-label": "bar",
  1156  					},
  1157  				},
  1158  				Spec: &networking.WorkloadEntry{
  1159  					Address: "1.1.1.1",
  1160  					Labels:  workloadLabel,
  1161  					Ports: map[string]uint32{
  1162  						"http": 80,
  1163  					},
  1164  					ServiceAccount: "scooby",
  1165  				},
  1166  			},
  1167  			out: &model.WorkloadInstance{
  1168  				Namespace: "ns1",
  1169  				Kind:      model.WorkloadEntryKind,
  1170  				Endpoint: &model.IstioEndpoint{
  1171  					Labels: map[string]string{
  1172  						"my-label":                  "bar",
  1173  						"app":                       "wle",
  1174  						"topology.istio.io/cluster": clusterID,
  1175  					},
  1176  					Address:        "1.1.1.1",
  1177  					ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby",
  1178  					TLSMode:        "istio",
  1179  					Namespace:      "ns1",
  1180  					Locality: model.Locality{
  1181  						ClusterID: cluster.ID(clusterID),
  1182  					},
  1183  				},
  1184  				PortMap: map[string]uint32{
  1185  					"http": 80,
  1186  				},
  1187  			},
  1188  		},
  1189  		{
  1190  			name: "augment labels",
  1191  			wle: config.Config{
  1192  				Meta: config.Meta{
  1193  					Namespace: "ns1",
  1194  				},
  1195  				Spec: &networking.WorkloadEntry{
  1196  					Address: "1.1.1.1",
  1197  					Labels:  workloadLabel,
  1198  					Ports: map[string]uint32{
  1199  						"http": 80,
  1200  					},
  1201  					Locality:       "region1/zone1/subzone1",
  1202  					Network:        "network1",
  1203  					ServiceAccount: "scooby",
  1204  				},
  1205  			},
  1206  			out: &model.WorkloadInstance{
  1207  				Namespace: "ns1",
  1208  				Kind:      model.WorkloadEntryKind,
  1209  				Endpoint: &model.IstioEndpoint{
  1210  					Labels: map[string]string{
  1211  						"app":                           "wle",
  1212  						"topology.kubernetes.io/region": "region1",
  1213  						"topology.kubernetes.io/zone":   "zone1",
  1214  						"topology.istio.io/subzone":     "subzone1",
  1215  						"topology.istio.io/network":     "network1",
  1216  						"topology.istio.io/cluster":     clusterID,
  1217  					},
  1218  					Address: "1.1.1.1",
  1219  					Network: "network1",
  1220  					Locality: model.Locality{
  1221  						Label:     "region1/zone1/subzone1",
  1222  						ClusterID: cluster.ID(clusterID),
  1223  					},
  1224  					ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby",
  1225  					TLSMode:        "istio",
  1226  					Namespace:      "ns1",
  1227  				},
  1228  				PortMap: map[string]uint32{
  1229  					"http": 80,
  1230  				},
  1231  			},
  1232  		},
  1233  		{
  1234  			name: "augment labels: networkID get from cb",
  1235  			wle: config.Config{
  1236  				Meta: config.Meta{
  1237  					Namespace: "ns1",
  1238  				},
  1239  				Spec: &networking.WorkloadEntry{
  1240  					Address: "1.1.1.1",
  1241  					Labels:  workloadLabel,
  1242  					Ports: map[string]uint32{
  1243  						"http": 80,
  1244  					},
  1245  					Locality:       "region1/zone1/subzone1",
  1246  					ServiceAccount: "scooby",
  1247  				},
  1248  			},
  1249  			getNetworkIDCb: func(IP string, labels labels.Instance) network.ID {
  1250  				return "cb-network1"
  1251  			},
  1252  			out: &model.WorkloadInstance{
  1253  				Namespace: "ns1",
  1254  				Kind:      model.WorkloadEntryKind,
  1255  				Endpoint: &model.IstioEndpoint{
  1256  					Labels: map[string]string{
  1257  						"app":                           "wle",
  1258  						"topology.kubernetes.io/region": "region1",
  1259  						"topology.kubernetes.io/zone":   "zone1",
  1260  						"topology.istio.io/subzone":     "subzone1",
  1261  						"topology.istio.io/network":     "cb-network1",
  1262  						"topology.istio.io/cluster":     clusterID,
  1263  					},
  1264  					Address: "1.1.1.1",
  1265  					Locality: model.Locality{
  1266  						Label:     "region1/zone1/subzone1",
  1267  						ClusterID: cluster.ID(clusterID),
  1268  					},
  1269  					ServiceAccount: "spiffe://cluster.local/ns/ns1/sa/scooby",
  1270  					TLSMode:        "istio",
  1271  					Namespace:      "ns1",
  1272  				},
  1273  				PortMap: map[string]uint32{
  1274  					"http": 80,
  1275  				},
  1276  			},
  1277  		},
  1278  	}
  1279  
  1280  	for _, tt := range workloadInstanceTests {
  1281  		t.Run(tt.name, func(t *testing.T) {
  1282  			s := &Controller{networkIDCallback: tt.getNetworkIDCb}
  1283  			instance := s.convertWorkloadEntryToWorkloadInstance(tt.wle, cluster.ID(clusterID))
  1284  			if err := compare(t, instance, tt.out); err != nil {
  1285  				t.Fatal(err)
  1286  			}
  1287  		})
  1288  	}
  1289  }
  1290  
  1291  func compare[T any](t testing.TB, actual, expected T) error {
  1292  	return util.Compare(jsonBytes(t, actual), jsonBytes(t, expected))
  1293  }
  1294  
  1295  func jsonBytes(t testing.TB, v any) []byte {
  1296  	data, err := json.MarshalIndent(v, "", " ")
  1297  	if err != nil {
  1298  		t.Fatal(t)
  1299  	}
  1300  	return data
  1301  }