func()

in pilot/pkg/networking/core/v1alpha3/gateway.go [61:185]


func (configgen *ConfigGeneratorImpl) buildGatewayListeners(builder *ListenerBuilder) *ListenerBuilder {
	if builder.node.MergedGateway == nil {
		log.Debugf("buildGatewayListeners: no gateways for router %v", builder.node.ID)
		return builder
	}

	mergedGateway := builder.node.MergedGateway
	log.Debugf("buildGatewayListeners: gateways after merging: %v", mergedGateway)

	actualWildcard, _ := getActualWildcardAndLocalHost(builder.node)
	errs := istiomultierror.New()
	// Mutable objects keyed by listener name so that we can build listeners at the end.
	mutableopts := make(map[string]mutableListenerOpts)
	proxyConfig := builder.node.Metadata.ProxyConfigOrDefault(builder.push.Mesh.DefaultConfig)
	for _, port := range mergedGateway.ServerPorts {
		// Skip ports we cannot bind to. Note that MergeGateways will already translate Service port to
		// targetPort, which handles the common case of exposing ports like 80 and 443 but listening on
		// higher numbered ports.
		if builder.node.IsUnprivileged() && port.Number < 1024 {
			log.Warnf("buildGatewayListeners: skipping privileged gateway port %d for node %s as it is an unprivileged pod",
				port.Number, builder.node.ID)
			continue
		}
		bind := actualWildcard
		if len(port.Bind) > 0 {
			bind = port.Bind
		}

		// NOTE: There is no gating here to check for the value of the QUIC feature flag. However,
		// they are created in MergeGatways only when the flag is set. So when it is turned off, the
		// MergedQUICTransportServers would be nil so that no listener would be created. It is written this way
		// to make testing a little easier.
		transportToServers := map[istionetworking.TransportProtocol]map[model.ServerPort]*model.MergedServers{
			istionetworking.TransportProtocolTCP:  mergedGateway.MergedServers,
			istionetworking.TransportProtocolQUIC: mergedGateway.MergedQUICTransportServers,
		}

		for transport, gwServers := range transportToServers {
			if gwServers == nil {
				log.Debugf("buildGatewayListeners: no gateway-server for transport %s at port %d", transport.String(), port)
				continue
			}

			// on a given port, we can either have plain text HTTP servers or
			// HTTPS/TLS servers with SNI. We cannot have a mix of http and https server on same port.
			// We can also have QUIC on a given port along with HTTPS/TLS on a given port. It does not
			// cause port-conflict as they use different transport protocols
			opts := &buildListenerOpts{
				push:       builder.push,
				proxy:      builder.node,
				bind:       bind,
				port:       &model.Port{Port: int(port.Number)},
				bindToPort: true,
				class:      istionetworking.ListenerClassGateway,
				transport:  transport,
			}
			lname := getListenerName(bind, int(port.Number), transport)
			p := protocol.Parse(port.Protocol)
			serversForPort := gwServers[port]
			if serversForPort == nil {
				continue
			}

			var newFilterChains []istionetworking.FilterChain
			switch transport {
			case istionetworking.TransportProtocolTCP:
				newFilterChains = configgen.buildGatewayTCPBasedFilterChains(builder, p, port, opts, serversForPort, proxyConfig, mergedGateway)
			case istionetworking.TransportProtocolQUIC:
				// Currently, we just assume that QUIC is HTTP/3 although that does not
				// have to be the case (it is just the most common case now, in the future
				// we will support more cases)
				newFilterChains = configgen.buildGatewayHTTP3FilterChains(builder, serversForPort, mergedGateway, proxyConfig, opts)
			}
			var mutable *MutableListener
			if mopts, exists := mutableopts[lname]; !exists {
				mutable = &MutableListener{
					MutableObjects: istionetworking.MutableObjects{
						// Note: buildListener creates filter chains but does not populate the filters in the chain; that's what
						// this is for.
						FilterChains: newFilterChains,
					},
				}
				mutableopts[lname] = mutableListenerOpts{mutable: mutable, opts: opts}
			} else {
				mopts.opts.filterChainOpts = append(mopts.opts.filterChainOpts, opts.filterChainOpts...)
				mopts.mutable.MutableObjects.FilterChains = append(mopts.mutable.MutableObjects.FilterChains, newFilterChains...)
				mutable = mopts.mutable
			}

			for cnum := range mutable.FilterChains {
				if mutable.FilterChains[cnum].ListenerProtocol == istionetworking.ListenerProtocolTCP {
					mutable.FilterChains[cnum].TCP = append(mutable.FilterChains[cnum].TCP, builder.authzCustomBuilder.BuildTCP()...)
					mutable.FilterChains[cnum].TCP = append(mutable.FilterChains[cnum].TCP, builder.authzBuilder.BuildTCP()...)
				}
			}
		}
	}
	listeners := make([]*listener.Listener, 0)
	for _, ml := range mutableopts {
		ml.mutable.Listener = buildListener(*ml.opts, core.TrafficDirection_OUTBOUND)
		log.Debugf("buildGatewayListeners: marshaling listener %q with %d filter chains",
			ml.mutable.Listener.GetName(), len(ml.mutable.Listener.GetFilterChains()))

		// Filters are serialized one time into an opaque struct once we have the complete list.
		if err := ml.mutable.build(builder, *ml.opts); err != nil {
			errs = multierror.Append(errs, fmt.Errorf("gateway omitting listener %q due to: %v", ml.mutable.Listener.Name, err.Error()))
			continue
		}
		listeners = append(listeners, ml.mutable.Listener)
	}
	// We'll try to return any listeners we successfully marshaled; if we have none, we'll emit the error we built up
	err := errs.ErrorOrNil()
	if err != nil {
		// we have some listeners to return, but we also have some errors; log them
		log.Info(err.Error())
	}

	if len(mutableopts) == 0 {
		log.Warnf("gateway has zero listeners for node %v", builder.node.ID)
		return builder
	}

	builder.gatewayListeners = listeners
	return builder
}