github.com/polarismesh/polaris@v1.17.8/apiserver/xdsserverv3/rds.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package xdsserverv3 19 20 import ( 21 corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 22 route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 23 v32 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 24 "github.com/envoyproxy/go-control-plane/pkg/cache/types" 25 "github.com/golang/protobuf/ptypes/wrappers" 26 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 27 "github.com/polarismesh/specification/source/go/api/v1/traffic_manage" 28 "google.golang.org/protobuf/types/known/wrapperspb" 29 30 "github.com/polarismesh/polaris/apiserver/xdsserverv3/resource" 31 "github.com/polarismesh/polaris/common/model" 32 "github.com/polarismesh/polaris/common/utils" 33 "github.com/polarismesh/polaris/service" 34 ) 35 36 // RDSBuilder . 37 type RDSBuilder struct { 38 client *resource.XDSClient 39 svr service.DiscoverServer 40 } 41 42 func (rds *RDSBuilder) Init(client *resource.XDSClient, svr service.DiscoverServer) { 43 rds.client = client 44 rds.svr = svr 45 } 46 47 func (rds *RDSBuilder) Generate(option *resource.BuildOption) (interface{}, error) { 48 var resources []types.Resource 49 50 switch rds.client.RunType { 51 case resource.RunTypeGateway: 52 // Envoy Gateway 场景只需要支持入流量场景处理即可 53 ret, err := rds.makeGatewayVirtualHosts(option) 54 if err != nil { 55 return nil, err 56 } 57 resources = ret 58 case resource.RunTypeSidecar: 59 resources = rds.makeSidecarVirtualHosts(option) 60 } 61 return resources, nil 62 } 63 64 func (rds *RDSBuilder) makeSidecarVirtualHosts(option *resource.BuildOption) []types.Resource { 65 // 每个 polaris serviceInfo 对应一个 virtualHost 66 var ( 67 routeConfs []types.Resource 68 hosts []*route.VirtualHost 69 ) 70 71 selfService := model.ServiceKey{ 72 Namespace: rds.client.GetSelfNamespace(), 73 Name: rds.client.GetSelfService(), 74 } 75 76 // step 1: 生成服务的 OUTBOUND 规则 77 services := option.Services 78 for svcKey, serviceInfo := range services { 79 vHost := &route.VirtualHost{ 80 Name: resource.MakeServiceName(svcKey, corev3.TrafficDirection_OUTBOUND), 81 Domains: resource.GenerateServiceDomains(serviceInfo), 82 Routes: rds.makeSidecarOutBoundRoutes(corev3.TrafficDirection_OUTBOUND, serviceInfo), 83 } 84 hosts = append(hosts, vHost) 85 } 86 87 routeConfiguration := &route.RouteConfiguration{ 88 Name: resource.OutBoundRouteConfigName, 89 ValidateClusters: &wrappers.BoolValue{ 90 Value: false, 91 }, 92 VirtualHosts: append(hosts, resource.BuildAllowAnyVHost()), 93 } 94 routeConfs = append(routeConfs, routeConfiguration) 95 96 // step 2: 生成 sidecar 所属服务的 INBOUND 规则 97 // 服务信息不存在或者不精确,不下发 InBound RDS 规则信息 98 if selfService.IsExact() { 99 routeConfs = append(routeConfs, &route.RouteConfiguration{ 100 Name: resource.InBoundRouteConfigName, 101 ValidateClusters: &wrappers.BoolValue{ 102 Value: false, 103 }, 104 VirtualHosts: []*route.VirtualHost{ 105 { 106 Name: resource.MakeServiceName(selfService, corev3.TrafficDirection_INBOUND), 107 Domains: []string{"*"}, 108 Routes: rds.makeSidecarInBoundRoutes(selfService, corev3.TrafficDirection_INBOUND), 109 }, 110 }, 111 }) 112 } 113 114 return routeConfs 115 } 116 117 func (rds *RDSBuilder) makeSidecarInBoundRoutes(selfService model.ServiceKey, 118 trafficDirection corev3.TrafficDirection) []*route.Route { 119 currentRoute := &route.Route{ 120 Match: &route.RouteMatch{ 121 PathSpecifier: &route.RouteMatch_Prefix{Prefix: "/"}, 122 }, 123 Action: &route.Route_Route{ 124 Route: &route.RouteAction{ 125 ClusterSpecifier: &route.RouteAction_WeightedClusters{ 126 WeightedClusters: &route.WeightedCluster{ 127 Clusters: []*route.WeightedCluster_ClusterWeight{ 128 { 129 Name: resource.MakeServiceName(selfService, trafficDirection), 130 Weight: wrapperspb.UInt32(100), 131 }, 132 }, 133 }, 134 }, 135 }, 136 }, 137 } 138 139 seacher := rds.svr.Cache().RateLimit() 140 limits, typedPerFilterConfig, err := resource.MakeSidecarLocalRateLimit(seacher, selfService) 141 if err == nil { 142 currentRoute.TypedPerFilterConfig = typedPerFilterConfig 143 currentRoute.GetRoute().RateLimits = limits 144 } 145 return []*route.Route{ 146 currentRoute, 147 } 148 } 149 150 // makeSidecarOutBoundRoutes . 151 func (rds *RDSBuilder) makeSidecarOutBoundRoutes(trafficDirection corev3.TrafficDirection, 152 serviceInfo *resource.ServiceInfo) []*route.Route { 153 var ( 154 routes []*route.Route 155 matchAllRoute *route.Route 156 ) 157 // 路由目前只处理 inbounds, 由于目前 envoy 获取不到自身服务数据,因此获取所有服务的被调规则 158 rules := resource.FilterInboundRouterRule(serviceInfo) 159 for _, rule := range rules { 160 var ( 161 matchAll bool 162 destinations []*traffic_manage.DestinationGroup 163 ) 164 for _, dest := range rule.GetDestinations() { 165 if !serviceInfo.MatchService(dest.GetNamespace(), dest.GetService()) { 166 continue 167 } 168 destinations = append(destinations, dest) 169 } 170 171 routeMatch := &route.RouteMatch{ 172 PathSpecifier: &route.RouteMatch_Prefix{Prefix: "/"}, 173 } 174 // 使用 sources 生成 routeMatch 175 for _, source := range rule.GetSources() { 176 if len(source.GetArguments()) == 0 { 177 matchAll = true 178 break 179 } 180 for _, arg := range source.GetArguments() { 181 if arg.Key == utils.MatchAll { 182 matchAll = true 183 break 184 } 185 } 186 if matchAll { 187 break 188 } else { 189 resource.BuildSidecarRouteMatch(routeMatch, source) 190 } 191 } 192 193 currentRoute := resource.MakeSidecarRoute(trafficDirection, routeMatch, serviceInfo, destinations) 194 if matchAll { 195 matchAllRoute = currentRoute 196 } else { 197 routes = append(routes, currentRoute) 198 } 199 } 200 if matchAllRoute == nil { 201 // 如果没有路由,会进入最后的默认处理 202 routes = append(routes, resource.MakeDefaultRoute(trafficDirection, serviceInfo.ServiceKey)) 203 } else { 204 routes = append(routes, matchAllRoute) 205 } 206 return routes 207 } 208 209 // ---------------------- Envoy Gateway ---------------------- // 210 func (rds *RDSBuilder) makeGatewayVirtualHosts(option *resource.BuildOption) ([]types.Resource, error) { 211 // 每个 polaris serviceInfo 对应一个 virtualHost 212 var ( 213 routeConfs []types.Resource 214 hosts []*route.VirtualHost 215 ) 216 217 routes, err := rds.makeGatewayRoutes(option, rds.client) 218 if err != nil { 219 return nil, err 220 } 221 if len(routes) == 0 { 222 return []types.Resource{}, nil 223 } 224 225 vHost := &route.VirtualHost{ 226 Name: "gateway-virtualhost", 227 Domains: resource.MakeServiceGatewayDomains(), 228 Routes: routes, 229 } 230 hosts = append(hosts, vHost) 231 routeConfiguration := &route.RouteConfiguration{ 232 Name: resource.OutBoundRouteConfigName, 233 VirtualHosts: hosts, 234 } 235 return append(routeConfs, routeConfiguration), nil 236 } 237 238 // makeGatewayRoutes builds the route.Route list for the envoy_gateway scenario 239 // In this scenario, it is mainly for the rule forwarding of path, /serviceA => serviceA 240 // Currently only routing rules that meet the following conditions support xds converted to envoy_gateway 241 // require 1: The calling service must match the GatewayService & GatewayNamespace in NodeProxy Metadata 242 // require 2: The $path parameter must be set in the request tag 243 // require 3: The information of the called service must be accurate, that is, a clear namespace and service 244 func (rds *RDSBuilder) makeGatewayRoutes(option *resource.BuildOption, 245 xdsNode *resource.XDSClient) ([]*route.Route, error) { 246 247 routes := make([]*route.Route, 0, 16) 248 callerService := xdsNode.GetSelfService() 249 callerNamespace := xdsNode.GetSelfNamespace() 250 selfService := model.ServiceKey{ 251 Namespace: callerNamespace, 252 Name: callerService, 253 } 254 if !selfService.IsExact() { 255 return nil, nil 256 } 257 258 routerCache := rds.svr.Cache().RoutingConfig() 259 routerRules := routerCache.ListRouterRule(callerService, callerNamespace) 260 for i := range routerRules { 261 rule := routerRules[i] 262 if rule.GetRoutingPolicy() != traffic_manage.RoutingPolicy_RulePolicy { 263 continue 264 } 265 266 for i := range rule.RuleRouting.Rules { 267 subRule := rule.RuleRouting.Rules[i] 268 // 先判断 dest 的服务是否满足目标 namespace 269 var ( 270 matchNamespace bool 271 findGatewaySource bool 272 ) 273 for _, dest := range subRule.GetDestinations() { 274 if dest.Namespace == callerNamespace && dest.Service != utils.MatchAll { 275 matchNamespace = true 276 } 277 } 278 if !matchNamespace { 279 continue 280 } 281 282 routeMatch := &route.RouteMatch{ 283 PathSpecifier: &route.RouteMatch_Prefix{Prefix: "/"}, 284 } 285 for _, source := range subRule.Sources { 286 if !isMatchGatewaySource(source, callerService, callerNamespace) { 287 continue 288 } 289 findGatewaySource = true 290 buildGatewayRouteMatch(routeMatch, source) 291 } 292 293 if !findGatewaySource { 294 continue 295 } 296 297 gatewayRoute := resource.MakeGatewayRoute(corev3.TrafficDirection_OUTBOUND, routeMatch, 298 subRule.GetDestinations()) 299 pathInfo := gatewayRoute.GetMatch().GetPath() 300 if pathInfo == "" { 301 pathInfo = gatewayRoute.GetMatch().GetSafeRegex().GetRegex() 302 } 303 304 seacher := rds.svr.Cache().RateLimit() 305 limits, typedPerFilterConfig, err := resource.MakeGatewayLocalRateLimit(seacher, pathInfo, selfService) 306 if err == nil { 307 gatewayRoute.TypedPerFilterConfig = typedPerFilterConfig 308 gatewayRoute.GetRoute().RateLimits = limits 309 } 310 routes = append(routes, gatewayRoute) 311 } 312 } 313 314 routes = append(routes, &route.Route{ 315 Match: &route.RouteMatch{ 316 PathSpecifier: &route.RouteMatch_Prefix{ 317 Prefix: "/", 318 }, 319 }, 320 Action: &route.Route_Route{ 321 Route: &route.RouteAction{ 322 ClusterSpecifier: &route.RouteAction_Cluster{ 323 Cluster: resource.PassthroughClusterName, 324 }, 325 }, 326 }, 327 }) 328 return routes, nil 329 } 330 331 func buildGatewayRouteMatch(routeMatch *route.RouteMatch, source *traffic_manage.SourceService) { 332 for i := range source.GetArguments() { 333 argument := source.GetArguments()[i] 334 if argument.Type == traffic_manage.SourceMatch_PATH { 335 if argument.Value.Type == apimodel.MatchString_EXACT { 336 routeMatch.PathSpecifier = &route.RouteMatch_Path{ 337 Path: argument.GetValue().GetValue().GetValue(), 338 } 339 } else if argument.Value.Type == apimodel.MatchString_REGEX { 340 routeMatch.PathSpecifier = &route.RouteMatch_SafeRegex{ 341 SafeRegex: &v32.RegexMatcher{ 342 Regex: argument.GetValue().GetValue().GetValue(), 343 }, 344 } 345 } 346 } 347 } 348 resource.BuildCommonRouteMatch(routeMatch, source) 349 } 350 351 func isMatchGatewaySource(source *traffic_manage.SourceService, svcName, svcNamespace string) bool { 352 var ( 353 existPathLabel bool 354 matchService bool 355 ) 356 357 args := source.GetArguments() 358 for i := range args { 359 if args[i].Type == traffic_manage.SourceMatch_PATH { 360 existPathLabel = true 361 break 362 } 363 } 364 365 matchService = source.Service == svcName && source.Namespace == svcNamespace 366 return existPathLabel && matchService 367 }