@@ -98,18 +98,25 @@ func ConvertGatewayResources(stores GatewayStores) (GatewayConversionResult, err
9898 if len (hosts ) == 0 {
9999 continue
100100 }
101- upstreams := gatewayServiceUpstreams (classConfig , listener , serviceByKey )
101+ source := RouteSource {
102+ Kind : "HTTPRoute" ,
103+ Namespace : route .Namespace ,
104+ Name : route .Name ,
105+ Class : classConfig .Name ,
106+ KubernetesClusterName : stores .ClusterName ,
107+ }
108+ upstreams := resolveGatewayUpstreams (gateway , listener , classConfig , serviceByKey )
102109 if len (upstreams ) == 0 {
110+ for _ , host := range hosts {
111+ result .Diagnostics = append (result .Diagnostics , GatewayDiagnostic {
112+ Host : host ,
113+ Source : source ,
114+ Reason : fmt .Sprintf ("no gateway address found for gateway %s/%s: empty status.addresses, no owned Service, no serviceName configured for class %s" , gateway .Namespace , gateway .Name , classConfig .Name ),
115+ })
116+ }
103117 continue
104118 }
105119 if denyReason := listenerSecretRefDenyReason (gateway , listener , stores .ReferenceGrants ); denyReason != "" {
106- source := RouteSource {
107- Kind : "HTTPRoute" ,
108- Namespace : route .Namespace ,
109- Name : route .Name ,
110- Class : classConfig .Name ,
111- KubernetesClusterName : stores .ClusterName ,
112- }
113120 for _ , host := range hosts {
114121 result .Diagnostics = append (result .Diagnostics , GatewayDiagnostic {
115122 Host : host ,
@@ -119,13 +126,6 @@ func ConvertGatewayResources(stores GatewayStores) (GatewayConversionResult, err
119126 }
120127 continue
121128 }
122- source := RouteSource {
123- Kind : "HTTPRoute" ,
124- Namespace : route .Namespace ,
125- Name : route .Name ,
126- Class : classConfig .Name ,
127- KubernetesClusterName : stores .ClusterName ,
128- }
129129 for _ , diagnostic := range listenerCertificateRefDiagnostics (source , listener , hosts ) {
130130 result .Diagnostics = append (result .Diagnostics , diagnostic )
131131 }
@@ -240,11 +240,67 @@ func hostMatchesGatewayListener(routeHost, listenerHost string) bool {
240240 return false
241241}
242242
243+ // resolveGatewayUpstreams discovers the Gateway Address (data-plane endpoints) for a
244+ // listener, in vendor-agnostic precedence order (see docs/adr/0002):
245+ // 1. Gateway.status.addresses (spec-guaranteed) with the listener port
246+ // 2. a Service owned by the Gateway (per-Gateway deployments)
247+ // 3. the static serviceName/serviceNamespace from config.json (escape hatch for
248+ // merged/shared deployments whose implementation does not publish status addresses)
249+ func resolveGatewayUpstreams (gateway * gatewayv1.Gateway , listener gatewayv1.Listener , classConfig GatewayClassConfig , services map [string ]* v1.Service ) []UpstreamEndpoint {
250+ if upstreams := gatewayStatusUpstreams (gateway , listener ); len (upstreams ) > 0 {
251+ return upstreams
252+ }
253+ if upstreams := ownedServiceUpstreams (gateway , listener , services ); len (upstreams ) > 0 {
254+ return upstreams
255+ }
256+ return gatewayServiceUpstreams (classConfig , listener , services )
257+ }
258+
259+ func gatewayStatusUpstreams (gateway * gatewayv1.Gateway , listener gatewayv1.Listener ) []UpstreamEndpoint {
260+ upstreams := []UpstreamEndpoint {}
261+ for _ , address := range gateway .Status .Addresses {
262+ if address .Value == "" {
263+ continue
264+ }
265+ upstreams = append (upstreams , UpstreamEndpoint {Host : address .Value , Port : uint32 (listener .Port )})
266+ }
267+ return uniqueUpstreams (upstreams )
268+ }
269+
270+ func ownedServiceUpstreams (gateway * gatewayv1.Gateway , listener gatewayv1.Listener , services map [string ]* v1.Service ) []UpstreamEndpoint {
271+ owned := []* v1.Service {}
272+ for _ , service := range services {
273+ if service .Namespace == gateway .Namespace && isOwnedByGateway (service , gateway ) {
274+ owned = append (owned , service )
275+ }
276+ }
277+ sort .Slice (owned , func (i , j int ) bool { return owned [i ].Name < owned [j ].Name })
278+ for _ , service := range owned {
279+ if upstreams := serviceUpstreams (service , listener ); len (upstreams ) > 0 {
280+ return upstreams
281+ }
282+ }
283+ return nil
284+ }
285+
286+ func isOwnedByGateway (service * v1.Service , gateway * gatewayv1.Gateway ) bool {
287+ for _ , owner := range service .OwnerReferences {
288+ if owner .Kind == "Gateway" && owner .Name == gateway .Name && strings .HasPrefix (owner .APIVersion , "gateway.networking.k8s.io/" ) {
289+ return true
290+ }
291+ }
292+ return false
293+ }
294+
243295func gatewayServiceUpstreams (classConfig GatewayClassConfig , listener gatewayv1.Listener , services map [string ]* v1.Service ) []UpstreamEndpoint {
244296 service := services [namespacedName (classConfig .ServiceNamespace , classConfig .ServiceName )]
245297 if service == nil {
246298 return nil
247299 }
300+ return serviceUpstreams (service , listener )
301+ }
302+
303+ func serviceUpstreams (service * v1.Service , listener gatewayv1.Listener ) []UpstreamEndpoint {
248304 port := servicePortForListener (service , listener )
249305 if port == 0 {
250306 return nil
0 commit comments