func ConvertToSidecarScope()

in pilot/pkg/model/sidecar.go [262:445]


func ConvertToSidecarScope(ps *PushContext, sidecarConfig *config.Config, configNamespace string) *SidecarScope {
	if sidecarConfig == nil {
		return DefaultSidecarScopeForNamespace(ps, configNamespace)
	}

	sidecar := sidecarConfig.Spec.(*networking.Sidecar)
	out := &SidecarScope{
		Name:               sidecarConfig.Name,
		Namespace:          configNamespace,
		Sidecar:            sidecar,
		configDependencies: make(map[uint64]struct{}),
		RootNamespace:      ps.Mesh.RootNamespace,
		Version:            ps.PushVersion,
	}

	out.AddConfigDependencies(ConfigKey{
		Kind:      gvk.Sidecar,
		Name:      sidecarConfig.Name,
		Namespace: sidecarConfig.Namespace,
	})

	egressConfigs := sidecar.Egress
	// If egress not set, setup a default listener
	if len(egressConfigs) == 0 {
		egressConfigs = append(egressConfigs, &networking.IstioEgressListener{Hosts: []string{"*/*"}})
	}
	out.EgressListeners = make([]*IstioEgressListenerWrapper, 0, len(egressConfigs))
	for _, e := range egressConfigs {
		out.EgressListeners = append(out.EgressListeners,
			convertIstioListenerToWrapper(ps, configNamespace, e))
	}

	// Now collect all the imported services across all egress listeners in
	// this sidecar crd. This is needed to generate CDS output
	out.services = make([]*Service, 0)
	type serviceIndex struct {
		svc   *Service
		index int // index record the position of the svc in slice
	}
	servicesAdded := make(map[host.Name]serviceIndex)
	addService := func(s *Service) {
		if s == nil {
			return
		}
		if foundSvc, found := servicesAdded[s.Hostname]; !found {
			out.AddConfigDependencies(ConfigKey{
				Kind:      gvk.ServiceEntry,
				Name:      string(s.Hostname),
				Namespace: s.Attributes.Namespace,
			})
			out.services = append(out.services, s)
			servicesAdded[s.Hostname] = serviceIndex{s, len(out.services) - 1}
		} else if foundSvc.svc.Attributes.Namespace == s.Attributes.Namespace && s.Ports != nil && len(s.Ports) > 0 {
			// merge the ports to service when each listener generates partial service
			// we only merge if the found service is in the same namespace as the one we're trying to add
			copied := foundSvc.svc.DeepCopy()
			for _, p := range s.Ports {
				found := false
				for _, osp := range copied.Ports {
					if p.Port == osp.Port {
						found = true
						break
					}
				}
				if !found {
					copied.Ports = append(copied.Ports, p)
				}
			}
			// replace service in slice
			out.services[foundSvc.index] = copied
			// Update index as well, so that future reads will merge into the new service
			foundSvc.svc = copied
			servicesAdded[foundSvc.svc.Hostname] = foundSvc
		}
	}

	for _, listener := range out.EgressListeners {
		// First add the explicitly requested services, which take priority
		for _, s := range listener.services {
			addService(s)
		}
		// add dependencies on delegate virtual services
		delegates := ps.DelegateVirtualServicesConfigKey(listener.virtualServices)
		for _, delegate := range delegates {
			out.AddConfigDependencies(delegate)
		}

		matchPort := needsPortMatch(listener)
		// Infer more possible destinations from virtual services
		// Services chosen here will not override services explicitly requested in listener.services.
		// That way, if there is ambiguity around what hostname to pick, a user can specify the one they
		// want in the hosts field, and the potentially random choice below won't matter
		for _, vs := range listener.virtualServices {
			v := vs.Spec.(*networking.VirtualService)
			out.AddConfigDependencies(ConfigKey{
				Kind:      gvk.VirtualService,
				Name:      vs.Name,
				Namespace: vs.Namespace,
			})

			for _, h := range virtualServiceDestinationHosts(v) {
				// Default to this hostname in our config namespace
				if s, ok := ps.ServiceIndex.HostnameAndNamespace[host.Name(h)][configNamespace]; ok {
					// This won't overwrite hostnames that have already been found eg because they were requested in hosts
					var vss *Service
					if matchPort {
						vss = serviceMatchingListenerPort(s, listener)
					} else {
						vss = s
					}
					if vss != nil {
						addService(vss)
					}
				} else {

					// We couldn't find the hostname in our config namespace
					// We have to pick one arbitrarily for now, so we'll pick the first namespace alphabetically
					// TODO: could we choose services more intelligently based on their ports?
					byNamespace := ps.ServiceIndex.HostnameAndNamespace[host.Name(h)]
					if len(byNamespace) == 0 {
						// This hostname isn't found anywhere
						log.Debugf("Could not find service hostname %s parsed from %s", h, vs.Key())
						continue
					}

					ns := make([]string, 0, len(byNamespace))
					for k := range byNamespace {
						if ps.IsServiceVisible(byNamespace[k], configNamespace) {
							ns = append(ns, k)
						}
					}
					if len(ns) > 0 {
						sort.Strings(ns)
						// Pick first namespace alphabetically
						// This won't overwrite hostnames that have already been found eg because they were requested in hosts
						var vss *Service
						if matchPort {
							vss = serviceMatchingListenerPort(byNamespace[ns[0]], listener)
						} else {
							vss = byNamespace[ns[0]]
						}
						if vss != nil {
							addService(vss)
						}
					}
				}
			}
		}
	}

	// Now that we have all the services that sidecars using this scope (in
	// this config namespace) will see, identify all the destinationRules
	// that these services need
	out.servicesByHostname = make(map[host.Name]*Service, len(out.services))
	out.destinationRules = make(map[host.Name][]*consolidatedDestRule)
	for _, s := range out.services {
		out.servicesByHostname[s.Hostname] = s
		drList := ps.destinationRule(configNamespace, s)
		if drList != nil {
			out.destinationRules[s.Hostname] = drList
			for _, dr := range drList {
				for _, key := range dr.from {
					out.AddConfigDependencies(ConfigKey{
						Kind:      gvk.DestinationRule,
						Name:      key.Name,
						Namespace: key.Namespace,
					})
				}
			}
		}
	}

	if sidecar.OutboundTrafficPolicy == nil {
		if ps.Mesh.OutboundTrafficPolicy != nil {
			out.OutboundTrafficPolicy = &networking.OutboundTrafficPolicy{
				Mode: networking.OutboundTrafficPolicy_Mode(ps.Mesh.OutboundTrafficPolicy.Mode),
			}
		}
	} else {
		out.OutboundTrafficPolicy = sidecar.OutboundTrafficPolicy
	}

	return out
}