1// Copyright 2017 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// This file describes the abstract model of services (and their instances) as 16// represented in Istio. This model is independent of the underlying platform 17// (Kubernetes, Mesos, etc.). Platform specific adapters found populate the 18// model object with various fields, from the metadata found in the platform. 19// The platform independent proxy code uses the representation in the model to 20// generate the configuration files for the Layer 7 proxy sidecar. The proxy 21// code is specific to individual proxy implementations 22 23package model 24 25import ( 26 "bytes" 27 "fmt" 28 "sort" 29 "strconv" 30 "strings" 31 "sync" 32 "time" 33 34 endpoint "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint" 35 "github.com/mitchellh/copystructure" 36 37 "istio.io/api/label" 38 39 authn "istio.io/api/authentication/v1alpha1" 40 41 "istio.io/istio/pkg/config/host" 42 "istio.io/istio/pkg/config/labels" 43 "istio.io/istio/pkg/config/protocol" 44 "istio.io/istio/pkg/config/visibility" 45) 46 47// Service describes an Istio service (e.g., catalog.mystore.com:8080) 48// Each service has a fully qualified domain name (FQDN) and one or more 49// ports where the service is listening for connections. *Optionally*, a 50// service can have a single load balancer/virtual IP address associated 51// with it, such that the DNS queries for the FQDN resolves to the virtual 52// IP address (a load balancer IP). 53// 54// E.g., in kubernetes, a service foo is associated with 55// foo.default.svc.cluster.local hostname, has a virtual IP of 10.0.1.1 and 56// listens on ports 80, 8080 57type Service struct { 58 // Attributes contains additional attributes associated with the service 59 // used mostly by mixer and RBAC for policy enforcement purposes. 60 Attributes ServiceAttributes 61 62 // Ports is the set of network ports where the service is listening for 63 // connections 64 Ports PortList `json:"ports,omitempty"` 65 66 // ServiceAccounts specifies the service accounts that run the service. 67 ServiceAccounts []string `json:"serviceAccounts,omitempty"` 68 69 // CreationTime records the time this service was created, if available. 70 CreationTime time.Time `json:"creationTime,omitempty"` 71 72 // Name of the service, e.g. "catalog.mystore.com" 73 Hostname host.Name `json:"hostname"` 74 75 // Address specifies the service IPv4 address of the load balancer 76 Address string `json:"address,omitempty"` 77 78 // Protect concurrent ClusterVIPs read/write 79 Mutex sync.RWMutex 80 81 // ClusterVIPs specifies the service address of the load balancer 82 // in each of the clusters where the service resides 83 ClusterVIPs map[string]string `json:"cluster-vips,omitempty"` 84 85 // Resolution indicates how the service instances need to be resolved before routing 86 // traffic. Most services in the service registry will use static load balancing wherein 87 // the proxy will decide the service instance that will receive the traffic. Service entries 88 // could either use DNS load balancing (i.e. proxy will query DNS server for the IP of the service) 89 // or use the passthrough model (i.e. proxy will forward the traffic to the network endpoint requested 90 // by the caller) 91 Resolution Resolution 92 93 // MeshExternal (if true) indicates that the service is external to the mesh. 94 // These services are defined using Istio's ServiceEntry spec. 95 MeshExternal bool 96} 97 98// Resolution indicates how the service instances need to be resolved before routing 99// traffic. 100type Resolution int 101 102const ( 103 // ClientSideLB implies that the proxy will decide the endpoint from its local lb pool 104 ClientSideLB Resolution = iota 105 // DNSLB implies that the proxy will resolve a DNS address and forward to the resolved address 106 DNSLB 107 // Passthrough implies that the proxy should forward traffic to the destination IP requested by the caller 108 Passthrough 109) 110 111// String converts Resolution in to String. 112func (resolution Resolution) String() string { 113 switch resolution { 114 case ClientSideLB: 115 return "ClientSide" 116 case DNSLB: 117 return "DNS" 118 case Passthrough: 119 return "Passthrough" 120 default: 121 return fmt.Sprintf("%d", int(resolution)) 122 } 123} 124 125const ( 126 // IstioDefaultConfigNamespace constant for default namespace 127 IstioDefaultConfigNamespace = "default" 128 129 // LocalityLabel indicates the region/zone/subzone of an instance. It is used to override the native 130 // registry's value. 131 // 132 // Note: because k8s labels does not support `/`, so we use `.` instead in k8s. 133 LocalityLabel = "istio-locality" 134 // k8s istio-locality label separator 135 k8sSeparator = "." 136) 137 138const ( 139 // TLSModeLabelShortname name used for determining endpoint level tls transport socket configuration 140 TLSModeLabelShortname = "tlsMode" 141 142 // DisabledTLSModeLabel implies that this endpoint should receive traffic as is (mostly plaintext) 143 DisabledTLSModeLabel = "disabled" 144 145 // IstioMutualTLSModeLabel implies that the endpoint is ready to receive Istio mTLS connections. 146 IstioMutualTLSModeLabel = "istio" 147 148 // IstioCanonicalServiceLabelName is the name of label for the Istio Canonical Service for a workload instance. 149 IstioCanonicalServiceLabelName = "service.istio.io/canonical-name" 150 151 // IstioCanonicalServiceRevisionLabelName is the name of label for the Istio Canonical Service revision for a workload instance. 152 IstioCanonicalServiceRevisionLabelName = "service.istio.io/canonical-revision" 153) 154 155// Port represents a network port where a service is listening for 156// connections. The port should be annotated with the type of protocol 157// used by the port. 158type Port struct { 159 // Name ascribes a human readable name for the port object. When a 160 // service has multiple ports, the name field is mandatory 161 Name string `json:"name,omitempty"` 162 163 // Port number where the service can be reached. Does not necessarily 164 // map to the corresponding port numbers for the instances behind the 165 // service. 166 Port int `json:"port"` 167 168 // Protocol to be used for the port. 169 Protocol protocol.Instance `json:"protocol,omitempty"` 170} 171 172// PortList is a set of ports 173type PortList []*Port 174 175// TrafficDirection defines whether traffic exists a service instance or enters a service instance 176type TrafficDirection string 177 178const ( 179 // TrafficDirectionInbound indicates inbound traffic 180 TrafficDirectionInbound TrafficDirection = "inbound" 181 // TrafficDirectionOutbound indicates outbound traffic 182 TrafficDirectionOutbound TrafficDirection = "outbound" 183 184 // trafficDirectionOutboundSrvPrefix the prefix for a DNS SRV type subset key 185 trafficDirectionOutboundSrvPrefix = string(TrafficDirectionOutbound) + "_" 186 // trafficDirectionInboundSrvPrefix the prefix for a DNS SRV type subset key 187 trafficDirectionInboundSrvPrefix = string(TrafficDirectionInbound) + "_" 188) 189 190// Probe represents a health probe associated with an instance of service. 191type Probe struct { 192 Port *Port `json:"port,omitempty"` 193 Path string `json:"path,omitempty"` 194} 195 196// ProbeList is a set of probes 197type ProbeList []*Probe 198 199// ServiceInstance represents an individual instance of a specific version 200// of a service. It binds a network endpoint (ip:port), the service 201// description (which is oblivious to various versions) and a set of labels 202// that describe the service version associated with this instance. 203// 204// Since a ServiceInstance has a single IstioEndpoint, which has a single port, 205// multiple ServiceInstances are required to represent a workload that listens 206// on multiple ports. 207// 208// The labels associated with a service instance are unique per a network endpoint. 209// There is one well defined set of labels for each service instance network endpoint. 210// 211// For example, the set of service instances associated with catalog.mystore.com 212// are modeled like this 213// --> IstioEndpoint(172.16.0.1:8888), Service(catalog.myservice.com), Labels(foo=bar) 214// --> IstioEndpoint(172.16.0.2:8888), Service(catalog.myservice.com), Labels(foo=bar) 215// --> IstioEndpoint(172.16.0.3:8888), Service(catalog.myservice.com), Labels(kitty=cat) 216// --> IstioEndpoint(172.16.0.4:8888), Service(catalog.myservice.com), Labels(kitty=cat) 217type ServiceInstance struct { 218 Service *Service `json:"service,omitempty"` 219 ServicePort *Port `json:"servicePort,omitempty"` 220 Endpoint *IstioEndpoint `json:"endpoint,omitempty"` 221} 222 223// DeepCopy creates a copy of ServiceInstance. 224func (instance *ServiceInstance) DeepCopy() *ServiceInstance { 225 return &ServiceInstance{ 226 Service: instance.Service.DeepCopy(), 227 Endpoint: instance.Endpoint.DeepCopy(), 228 ServicePort: &Port{ 229 Name: instance.ServicePort.Name, 230 Port: instance.ServicePort.Port, 231 Protocol: instance.ServicePort.Protocol, 232 }, 233 } 234} 235 236// GetLocalityLabelOrDefault returns the locality from the supplied label, or falls back to 237// the supplied default locality if the supplied label is empty. Because Kubernetes 238// labels don't support `/`, we replace "." with "/" in the supplied label as a workaround. 239func GetLocalityLabelOrDefault(label, defaultLabel string) string { 240 if len(label) > 0 { 241 // if there are /'s present we don't need to replace 242 if strings.Contains(label, "/") { 243 return label 244 } 245 // replace "." with "/" 246 return strings.Replace(label, k8sSeparator, "/", -1) 247 } 248 return defaultLabel 249} 250 251// Locality information for an IstioEndpoint 252type Locality struct { 253 // Label for locality on the endpoint. This is a "/" separated string. 254 Label string 255 256 // ClusterID where the endpoint is located 257 ClusterID string 258} 259 260// IstioEndpoint defines a network address (IP:port) associated with an instance of the 261// service. A service has one or more instances each running in a 262// container/VM/pod. If a service has multiple ports, then the same 263// instance IP is expected to be listening on multiple ports (one per each 264// service port). Note that the port associated with an instance does not 265// have to be the same as the port associated with the service. Depending 266// on the network setup (NAT, overlays), this could vary. 267// 268// For e.g., if catalog.mystore.com is accessible through port 80 and 8080, 269// and it maps to an instance with IP 172.16.0.1, such that connections to 270// port 80 are forwarded to port 55446, and connections to port 8080 are 271// forwarded to port 33333, 272// 273// then internally, we have two two endpoint structs for the 274// service catalog.mystore.com 275// --> 172.16.0.1:54546 (with ServicePort pointing to 80) and 276// --> 172.16.0.1:33333 (with ServicePort pointing to 8080) 277// 278// TODO: Investigate removing ServiceInstance entirely. 279type IstioEndpoint struct { 280 // Labels points to the workload or deployment labels. 281 Labels labels.Instance 282 283 // Address is the address of the endpoint, using envoy proto. 284 Address string 285 286 // ServicePortName tracks the name of the port, this is used to select the IstioEndpoint by service port. 287 ServicePortName string 288 289 // UID identifies the workload, for telemetry purpose. 290 UID string 291 292 // EnvoyEndpoint is a cached LbEndpoint, converted from the data, to 293 // avoid recomputation 294 EnvoyEndpoint *endpoint.LbEndpoint 295 296 // ServiceAccount holds the associated service account. 297 ServiceAccount string 298 299 // Network holds the network where this endpoint is present 300 Network string 301 302 // The locality where the endpoint is present. 303 Locality Locality 304 305 // EndpointPort is the port where the workload is listening, can be different 306 // from the service port. 307 EndpointPort uint32 308 309 // The load balancing weight associated with this endpoint. 310 LbWeight uint32 311 312 // TLSMode endpoint is injected with istio sidecar and ready to configure Istio mTLS 313 TLSMode string 314} 315 316// ServiceAttributes represents a group of custom attributes of the service. 317type ServiceAttributes struct { 318 // ServiceRegistry indicates the backing service registry system where this service 319 // was sourced from. 320 // TODO: move the ServiceRegistry type from platform.go to model 321 ServiceRegistry string 322 // Name is "destination.service.name" attribute 323 Name string 324 // Namespace is "destination.service.namespace" attribute 325 Namespace string 326 // UID is "destination.service.uid" attribute 327 UID string 328 // ExportTo defines the visibility of Service in 329 // a namespace when the namespace is imported. 330 ExportTo map[visibility.Instance]bool 331 332 // For Kubernetes platform 333 334 // ClusterExternalAddresses is a mapping between a cluster name and the external 335 // address(es) to access the service from outside the cluster. 336 // Used by the aggregator to aggregate the Attributes.ClusterExternalAddresses 337 // for clusters where the service resides 338 ClusterExternalAddresses map[string][]string 339 340 // ClusterExternalPorts is a mapping between a cluster name and the service port 341 // to node port mappings for a given service. When accessing the service via 342 // node port IPs, we need to use the kubernetes assigned node ports of the service 343 // The port that the user provides in the meshNetworks config is the service port. 344 // We translate that to the appropriate node port here. 345 ClusterExternalPorts map[string]map[uint32]uint32 346} 347 348// ServiceDiscovery enumerates Istio service instances. 349// nolint: lll 350//go:generate counterfeiter -o ../networking/core/v1alpha3/fakes/fake_service_discovery.gen.go --fake-name ServiceDiscovery . ServiceDiscovery 351type ServiceDiscovery interface { 352 // Services list declarations of all services in the system 353 Services() ([]*Service, error) 354 355 // GetService retrieves a service by host name if it exists 356 GetService(hostname host.Name) (*Service, error) 357 358 // InstancesByPort retrieves instances for a service on the given ports with labels that match 359 // any of the supplied labels. All instances match an empty tag list. 360 // 361 // For example, consider an example of catalog.mystore.com: 362 // Instances(catalog.myservice.com, 80) -> 363 // --> IstioEndpoint(172.16.0.1:8888), Service(catalog.myservice.com), Labels(foo=bar) 364 // --> IstioEndpoint(172.16.0.2:8888), Service(catalog.myservice.com), Labels(foo=bar) 365 // --> IstioEndpoint(172.16.0.3:8888), Service(catalog.myservice.com), Labels(kitty=cat) 366 // --> IstioEndpoint(172.16.0.4:8888), Service(catalog.myservice.com), Labels(kitty=cat) 367 // 368 // Calling Instances with specific labels returns a trimmed list. 369 // e.g., Instances(catalog.myservice.com, 80, foo=bar) -> 370 // --> IstioEndpoint(172.16.0.1:8888), Service(catalog.myservice.com), Labels(foo=bar) 371 // --> IstioEndpoint(172.16.0.2:8888), Service(catalog.myservice.com), Labels(foo=bar) 372 // 373 // Similar concepts apply for calling this function with a specific 374 // port, hostname and labels. 375 // 376 // Introduced in Istio 0.8. It is only called with 1 port. 377 // CDS (clusters.go) calls it for building 'dnslb' type clusters. 378 // EDS calls it for building the endpoints result. 379 // Consult istio-dev before using this for anything else (except debugging/tools) 380 InstancesByPort(svc *Service, servicePort int, labels labels.Collection) ([]*ServiceInstance, error) 381 382 // GetProxyServiceInstances returns the service instances that co-located with a given Proxy 383 // 384 // Co-located generally means running in the same network namespace and security context. 385 // 386 // A Proxy operating as a Sidecar will return a non-empty slice. A stand-alone Proxy 387 // will return an empty slice. 388 // 389 // There are two reasons why this returns multiple ServiceInstances instead of one: 390 // - A ServiceInstance has a single IstioEndpoint which has a single Port. But a Service 391 // may have many ports. So a workload implementing such a Service would need 392 // multiple ServiceInstances, one for each port. 393 // - A single workload may implement multiple logical Services. 394 // 395 // In the second case, multiple services may be implemented by the same physical port number, 396 // though with a different ServicePort and IstioEndpoint for each. If any of these overlapping 397 // services are not HTTP or H2-based, behavior is undefined, since the listener may not be able to 398 // determine the intended destination of a connection without a Host header on the request. 399 GetProxyServiceInstances(*Proxy) ([]*ServiceInstance, error) 400 401 GetProxyWorkloadLabels(*Proxy) (labels.Collection, error) 402 403 // ManagementPorts lists set of management ports associated with an IPv4 address. 404 // These management ports are typically used by the platform for out of band management 405 // tasks such as health checks, etc. In a scenario where the proxy functions in the 406 // transparent mode (traps all traffic to and from the service instance IP address), 407 // the configuration generated for the proxy will not manipulate traffic destined for 408 // the management ports 409 ManagementPorts(addr string) PortList 410 411 // WorkloadHealthCheckInfo lists set of probes associated with an IPv4 address. 412 // These probes are used by the platform to identify requests that are performing 413 // health checks. 414 WorkloadHealthCheckInfo(addr string) ProbeList 415 416 // GetIstioServiceAccounts returns a list of service accounts looked up from 417 // the specified service hostname and ports. 418 // Deprecated - service account tracking moved to XdsServer, incremental. 419 GetIstioServiceAccounts(svc *Service, ports []int) []string 420} 421 422// Match returns true if port matches with authentication port selector criteria. 423func (port Port) Match(portSelector *authn.PortSelector) bool { 424 if portSelector == nil { 425 return true 426 } 427 switch portSelector.Port.(type) { 428 case *authn.PortSelector_Name: 429 return portSelector.GetName() == port.Name 430 case *authn.PortSelector_Number: 431 return portSelector.GetNumber() == uint32(port.Port) 432 default: 433 return false 434 } 435} 436 437// GetNames returns port names 438func (ports PortList) GetNames() []string { 439 names := make([]string, 0, len(ports)) 440 for _, port := range ports { 441 names = append(names, port.Name) 442 } 443 return names 444} 445 446// Get retrieves a port declaration by name 447func (ports PortList) Get(name string) (*Port, bool) { 448 for _, port := range ports { 449 if port.Name == name { 450 return port, true 451 } 452 } 453 return nil, false 454} 455 456// GetByPort retrieves a port declaration by port value 457func (ports PortList) GetByPort(num int) (*Port, bool) { 458 for _, port := range ports { 459 if port.Port == num && port.Protocol != protocol.UDP { 460 return port, true 461 } 462 } 463 return nil, false 464} 465 466// External predicate checks whether the service is external 467func (s *Service) External() bool { 468 return s.MeshExternal 469} 470 471// Key generates a unique string referencing service instances for a given port and labels. 472// The separator character must be exclusive to the regular expressions allowed in the 473// service declaration. 474// Deprecated 475func (s *Service) Key(port *Port, l labels.Instance) string { 476 // TODO: check port is non nil and membership of port in service 477 return ServiceKey(s.Hostname, PortList{port}, labels.Collection{l}) 478} 479 480// ServiceKey generates a service key for a collection of ports and labels 481// Deprecated 482// 483// Interface wants to turn `Name` into `fmt.Stringer`, completely defeating the purpose of the type alias. 484// nolint: interfacer 485func ServiceKey(hostname host.Name, servicePorts PortList, labelsList labels.Collection) string { 486 // example: name.namespace|http|env=prod;env=test,version=my-v1 487 var buffer bytes.Buffer 488 buffer.WriteString(string(hostname)) 489 np := len(servicePorts) 490 nt := len(labelsList) 491 492 if nt == 1 && labelsList[0] == nil { 493 nt = 0 494 } 495 496 if np == 0 && nt == 0 { 497 return buffer.String() 498 } else if np == 1 && nt == 0 && servicePorts[0].Name == "" { 499 return buffer.String() 500 } else { 501 buffer.WriteString("|") 502 } 503 504 if np > 0 { 505 ports := make([]string, np) 506 for i := 0; i < np; i++ { 507 ports[i] = servicePorts[i].Name 508 } 509 sort.Strings(ports) 510 for i := 0; i < np; i++ { 511 if i > 0 { 512 buffer.WriteString(",") 513 } 514 buffer.WriteString(ports[i]) 515 } 516 } 517 518 if nt > 0 { 519 buffer.WriteString("|") 520 l := make([]string, nt) 521 for i := 0; i < nt; i++ { 522 l[i] = labelsList[i].String() 523 } 524 sort.Strings(l) 525 for i := 0; i < nt; i++ { 526 if i > 0 { 527 buffer.WriteString(";") 528 } 529 buffer.WriteString(l[i]) 530 } 531 } 532 return buffer.String() 533} 534 535// ParseServiceKey is the inverse of the Service.String() method 536// Deprecated 537func ParseServiceKey(s string) (hostname host.Name, ports PortList, lc labels.Collection) { 538 parts := strings.Split(s, "|") 539 hostname = host.Name(parts[0]) 540 541 var names []string 542 if len(parts) > 1 { 543 names = strings.Split(parts[1], ",") 544 } else { 545 names = []string{""} 546 } 547 548 for _, name := range names { 549 ports = append(ports, &Port{Name: name}) 550 } 551 552 if len(parts) > 2 && len(parts[2]) > 0 { 553 for _, tag := range strings.Split(parts[2], ";") { 554 lc = append(lc, labels.Parse(tag)) 555 } 556 } 557 return 558} 559 560// BuildSubsetKey generates a unique string referencing service instances for a given service name, a subset and a port. 561// The proxy queries Pilot with this key to obtain the list of instances in a subset. 562func BuildSubsetKey(direction TrafficDirection, subsetName string, hostname host.Name, port int) string { 563 return string(direction) + "|" + strconv.Itoa(port) + "|" + subsetName + "|" + string(hostname) 564} 565 566// BuildDNSSrvSubsetKey generates a unique string referencing service instances for a given service name, a subset and a port. 567// The proxy queries Pilot with this key to obtain the list of instances in a subset. 568// This is used only for the SNI-DNAT router. Do not use for other purposes. 569// The DNS Srv format of the cluster is also used as the default SNI string for Istio mTLS connections 570func BuildDNSSrvSubsetKey(direction TrafficDirection, subsetName string, hostname host.Name, port int) string { 571 return string(direction) + "_." + strconv.Itoa(port) + "_." + subsetName + "_." + string(hostname) 572} 573 574// IsValidSubsetKey checks if a string is valid for subset key parsing. 575func IsValidSubsetKey(s string) bool { 576 return strings.Count(s, "|") == 3 577} 578 579// ParseSubsetKey is the inverse of the BuildSubsetKey method 580func ParseSubsetKey(s string) (direction TrafficDirection, subsetName string, hostname host.Name, port int) { 581 var parts []string 582 dnsSrvMode := false 583 // This could be the DNS srv form of the cluster that uses outbound_.port_.subset_.hostname 584 // Since we do not want every callsite to implement the logic to differentiate between the two forms 585 // we add an alternate parser here. 586 if strings.HasPrefix(s, trafficDirectionOutboundSrvPrefix) || 587 strings.HasPrefix(s, trafficDirectionInboundSrvPrefix) { 588 parts = strings.SplitN(s, ".", 4) 589 dnsSrvMode = true 590 } else { 591 parts = strings.Split(s, "|") 592 } 593 594 if len(parts) < 4 { 595 return 596 } 597 598 direction = TrafficDirection(strings.TrimSuffix(parts[0], "_")) 599 port, _ = strconv.Atoi(strings.TrimSuffix(parts[1], "_")) 600 subsetName = parts[2] 601 602 if dnsSrvMode { 603 subsetName = strings.TrimSuffix(parts[2], "_") 604 } 605 606 hostname = host.Name(parts[3]) 607 return 608} 609 610// GetServiceAddressForProxy returns a Service's IP address specific to the cluster where the node resides 611func (s *Service) GetServiceAddressForProxy(node *Proxy) string { 612 s.Mutex.RLock() 613 defer s.Mutex.RUnlock() 614 if node.ClusterID != "" && s.ClusterVIPs[node.ClusterID] != "" { 615 return s.ClusterVIPs[node.ClusterID] 616 } 617 return s.Address 618} 619 620// GetTLSModeFromEndpointLabels returns the value of the label 621// security.istio.io/tlsMode if set. Do not return Enums or constants 622// from this function as users could provide values other than istio/disabled 623// and apply custom transport socket matchers here. 624func GetTLSModeFromEndpointLabels(labels map[string]string) string { 625 if labels != nil { 626 if val, exists := labels[label.TLSMode]; exists { 627 return val 628 } 629 } 630 return DisabledTLSModeLabel 631} 632 633// DeepCopy creates a clone of Service. 634// TODO : See if there is any efficient alternative to this function - copystructure can not be used as is because 635// Service has sync.RWMutex that can not be copied. 636func (s *Service) DeepCopy() *Service { 637 attrs := copyInternal(s.Attributes) 638 ports := copyInternal(s.Ports) 639 accounts := copyInternal(s.ServiceAccounts) 640 clusterVIPs := copyInternal(s.ClusterVIPs) 641 642 return &Service{ 643 Attributes: attrs.(ServiceAttributes), 644 Ports: ports.(PortList), 645 ServiceAccounts: accounts.([]string), 646 CreationTime: s.CreationTime, 647 Hostname: s.Hostname, 648 Address: s.Address, 649 ClusterVIPs: clusterVIPs.(map[string]string), 650 Resolution: s.Resolution, 651 MeshExternal: s.MeshExternal, 652 } 653} 654 655// DeepCopy creates a clone of IstioEndpoint. 656func (ep *IstioEndpoint) DeepCopy() *IstioEndpoint { 657 return copyInternal(ep).(*IstioEndpoint) 658} 659 660func copyInternal(v interface{}) interface{} { 661 copied, err := copystructure.Copy(v) 662 if err != nil { 663 // There are 2 locations where errors are generated in copystructure.Copy: 664 // * The reflection walk over the structure fails, which should never happen 665 // * A configurable copy function returns an error. This is only used for copying times, which never returns an error. 666 // Therefore, this should never happen 667 panic(err) 668 } 669 return copied 670} 671