diff --git a/pilot/pkg/features/pilot.go b/pilot/pkg/features/pilot.go index fe9e7553f144..11fda9b55637 100644 --- a/pilot/pkg/features/pilot.go +++ b/pilot/pkg/features/pilot.go @@ -176,6 +176,13 @@ var ( "If enabled, pilot will attempt to limit unnecessary pushes by determining what proxies "+ "a config or endpoint update will impact.", ) + + ScopeGatewayToNamespace = env.RegisterBoolVar( + "PILOT_SCOPE_GATEWAY_TO_NAMESPACE", + false, + "If enabled, a gateway workload can only select gateway resources in the same namespace. "+ + "Gateways with same selectors in different namespaces will not be applicable.", + ) ) var ( diff --git a/pilot/pkg/model/push_context.go b/pilot/pkg/model/push_context.go index d8cd0f55a72b..5698db834a34 100644 --- a/pilot/pkg/model/push_context.go +++ b/pilot/pkg/model/push_context.go @@ -21,6 +21,7 @@ import ( networking "istio.io/api/networking/v1alpha3" + "istio.io/istio/pilot/pkg/features" "istio.io/istio/pilot/pkg/monitoring" "istio.io/istio/pkg/config/constants" "istio.io/istio/pkg/config/host" @@ -72,6 +73,8 @@ type PushContext struct { sidecarsByNamespace map[string][]*SidecarScope // envoy filters for each namespace including global config namespace envoyFiltersByNamespace map[string][]*EnvoyFilterWrapper + // gateways for each namespace + gatewaysByNamespace map[string][]Config ////////// END //////// // The following data is either a global index or used in the inbound path. @@ -348,6 +351,7 @@ func NewPushContext() *PushContext { }, sidecarsByNamespace: map[string][]*SidecarScope{}, envoyFiltersByNamespace: map[string][]*EnvoyFilterWrapper{}, + gatewaysByNamespace: map[string][]Config{}, ServiceByHostnameAndNamespace: map[host.Name]map[string]*Service{}, ProxyStatus: map[string]map[string]ProxyPushStatus{}, @@ -631,6 +635,12 @@ func (ps *PushContext) InitContext(env *Environment) error { return err } + if features.ScopeGatewayToNamespace.Get() { + if err = ps.initGateways(env); err != nil { + return err + } + } + // Must be initialized in the end if err = ps.initSidecarScopes(env); err != nil { return err @@ -1061,3 +1071,43 @@ func (ps *PushContext) EnvoyFilters(proxy *Proxy) []*EnvoyFilterWrapper { } return out } + +// pre computes gateways per namespace +func (ps *PushContext) initGateways(env *Environment) error { + gatewayConfigs, err := env.List(Gateway.Type, NamespaceAll) + if err != nil { + return err + } + + sortConfigByCreationTime(gatewayConfigs) + + ps.gatewaysByNamespace = make(map[string][]Config) + for _, gatewayConfig := range gatewayConfigs { + if _, exists := ps.gatewaysByNamespace[gatewayConfig.Namespace]; !exists { + ps.gatewaysByNamespace[gatewayConfig.Namespace] = make([]Config, 0) + } + ps.gatewaysByNamespace[gatewayConfig.Namespace] = append(ps.gatewaysByNamespace[gatewayConfig.Namespace], gatewayConfig) + } + return nil +} + +func (ps *PushContext) Gateways(proxy *Proxy) []Config { + // this should never happen + if proxy == nil { + return nil + } + out := make([]Config, 0) + for _, cfg := range ps.gatewaysByNamespace[proxy.ConfigNamespace] { + gw := cfg.Spec.(*networking.Gateway) + if gw.GetSelector() == nil { + // no selector. Applies to all workloads asking for the gateway + out = append(out, cfg) + } else { + gatewaySelector := labels.Instance(gw.GetSelector()) + if proxy.WorkloadLabels.IsSupersetOf(gatewaySelector) { + out = append(out, cfg) + } + } + } + return out +} diff --git a/pilot/pkg/networking/core/v1alpha3/gateway.go b/pilot/pkg/networking/core/v1alpha3/gateway.go index 7ef5828007be..f5713999da2f 100644 --- a/pilot/pkg/networking/core/v1alpha3/gateway.go +++ b/pilot/pkg/networking/core/v1alpha3/gateway.go @@ -48,15 +48,18 @@ func (configgen *ConfigGeneratorImpl) buildGatewayListeners( node *model.Proxy, push *model.PushContext, builder *ListenerBuilder) *ListenerBuilder { - // collect workload labels - workloadInstances := node.ServiceInstances - var workloadLabels labels.Collection - for _, w := range workloadInstances { - workloadLabels = append(workloadLabels, w.Labels) + var gatewaysForWorkload []model.Config + if features.ScopeGatewayToNamespace.Get() { + gatewaysForWorkload = push.Gateways(node) + } else { + var workloadLabels labels.Collection + for _, w := range node.ServiceInstances { + workloadLabels = append(workloadLabels, w.Labels) + } + gatewaysForWorkload = env.Gateways(workloadLabels) } - gatewaysForWorkload := env.Gateways(workloadLabels) if len(gatewaysForWorkload) == 0 { log.Debuga("buildGatewayListeners: no gateways for router ", node.ID) return builder @@ -133,7 +136,7 @@ func (configgen *ConfigGeneratorImpl) buildGatewayListeners( // end shady logic var si *model.ServiceInstance - for _, w := range workloadInstances { + for _, w := range node.ServiceInstances { if w.Endpoint.Port == int(portNumber) { si = w break