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  }