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
}