| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package grpcgen |
| |
| import ( |
| "fmt" |
| "github.com/apache/dubbo-kubernetes/dubbod/planet/pkg/util/protoconv" |
| discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" |
| "strconv" |
| "strings" |
| |
| "github.com/apache/dubbo-kubernetes/dubbod/planet/pkg/model" |
| route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" |
| ) |
| |
| func (g *GrpcConfigGenerator) BuildHTTPRoutes(node *model.Proxy, push *model.PushContext, routeNames []string) model.Resources { |
| resp := model.Resources{} |
| for _, routeName := range routeNames { |
| if rc := buildHTTPRoute(node, push, routeName); rc != nil { |
| resp = append(resp, &discovery.Resource{ |
| Name: routeName, |
| Resource: protoconv.MessageToAny(rc), |
| }) |
| } |
| } |
| return resp |
| } |
| |
| func buildHTTPRoute(node *model.Proxy, push *model.PushContext, routeName string) *route.RouteConfiguration { |
| // For proxyless gRPC inbound routes, routeName is just the port number (e.g., "17070") |
| // For outbound routes, routeName is cluster format (outbound|port||hostname) |
| _, err := strconv.Atoi(routeName) |
| if err != nil { |
| // Try to parse as cluster naming format (outbound|port||hostname) |
| _, _, hostname, parsedPort := model.ParseSubsetKey(routeName) |
| if hostname == "" || parsedPort == 0 { |
| log.Warnf("failed to parse route name %v", routeName) |
| return nil |
| } |
| |
| // Build outbound route configuration for gRPC proxyless |
| // This is used by ApiListener to route traffic to the correct cluster |
| svc := push.ServiceForHostname(node, hostname) |
| if svc == nil { |
| log.Warnf("buildHTTPRoute: service not found for hostname %s", hostname) |
| return nil |
| } |
| |
| // Build VirtualHost Domains for outbound route |
| // CRITICAL: Domains must match the xDS URL hostname for gRPC xDS client |
| hostStr := string(hostname) |
| domains := []string{ |
| fmt.Sprintf("%s:%d", hostStr, parsedPort), // FQDN with port - MOST SPECIFIC |
| hostStr, // Full FQDN |
| } |
| // Add short name if different from FQDN |
| hostParts := strings.Split(hostStr, ".") |
| if len(hostParts) > 0 && hostParts[0] != hostStr { |
| shortName := hostParts[0] |
| domains = append(domains, fmt.Sprintf("%s:%d", shortName, parsedPort)) // Short name with port |
| domains = append(domains, shortName) // Short name |
| } |
| domains = append(domains, "*") // Wildcard for any domain - LEAST SPECIFIC |
| |
| return &route.RouteConfiguration{ |
| Name: routeName, |
| VirtualHosts: []*route.VirtualHost{ |
| { |
| Name: fmt.Sprintf("%s|http|%d", hostStr, parsedPort), |
| Domains: domains, |
| Routes: []*route.Route{ |
| { |
| Match: &route.RouteMatch{ |
| PathSpecifier: &route.RouteMatch_Prefix{ |
| Prefix: "/", |
| }, |
| }, |
| Action: &route.Route_Route{ |
| Route: &route.RouteAction{ |
| ClusterSpecifier: &route.RouteAction_Cluster{ |
| Cluster: routeName, // Use routeName (cluster name) |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| } |
| } |
| |
| // Build minimal route configuration for proxyless gRPC inbound listener |
| // NonForwardingAction indicates this is an inbound listener that should handle requests directly |
| return &route.RouteConfiguration{ |
| Name: routeName, |
| VirtualHosts: []*route.VirtualHost{ |
| { |
| Name: "inbound|http|" + routeName, |
| Domains: []string{"*"}, |
| Routes: []*route.Route{ |
| { |
| Match: &route.RouteMatch{ |
| PathSpecifier: &route.RouteMatch_Prefix{ |
| Prefix: "/", |
| }, |
| }, |
| Action: &route.Route_NonForwardingAction{}, |
| }, |
| }, |
| }, |
| }, |
| } |
| } |