github.com/polarismesh/polaris@v1.17.8/apiserver/xdsserverv3/resource/help.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 resource 19 20 import ( 21 "encoding/hex" 22 "fmt" 23 "math" 24 "strconv" 25 "strings" 26 "time" 27 28 accesslog "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" 29 cluster "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 30 core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 31 corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 32 listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 33 ratelimitconfv3 "github.com/envoyproxy/go-control-plane/envoy/config/ratelimit/v3" 34 route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 35 filev3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" 36 envoy_extensions_common_ratelimit_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/ratelimit/v3" 37 ratelimitv32 "github.com/envoyproxy/go-control-plane/envoy/extensions/common/ratelimit/v3" 38 lrl "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/local_ratelimit/v3" 39 ratelimitfilter "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3" 40 routerv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" 41 hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 42 tcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" 43 v32 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 44 envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" 45 typev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" 46 resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" 47 "github.com/envoyproxy/go-control-plane/pkg/wellknown" 48 "github.com/golang/protobuf/ptypes" 49 _struct "github.com/golang/protobuf/ptypes/struct" 50 "github.com/golang/protobuf/ptypes/wrappers" 51 apifault "github.com/polarismesh/specification/source/go/api/v1/fault_tolerance" 52 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 53 apiservice "github.com/polarismesh/specification/source/go/api/v1/service_manage" 54 "github.com/polarismesh/specification/source/go/api/v1/traffic_manage" 55 apitraffic "github.com/polarismesh/specification/source/go/api/v1/traffic_manage" 56 "google.golang.org/protobuf/proto" 57 "google.golang.org/protobuf/types/known/anypb" 58 "google.golang.org/protobuf/types/known/durationpb" 59 "google.golang.org/protobuf/types/known/wrapperspb" 60 61 types "github.com/polarismesh/polaris/cache/api" 62 "github.com/polarismesh/polaris/common/model" 63 "github.com/polarismesh/polaris/common/utils" 64 ) 65 66 const ( 67 PassthroughClusterName = "PassthroughCluster" 68 RouteConfigName = "polaris-router" 69 OutBoundRouteConfigName = "polaris-outbound-router" 70 InBoundRouteConfigName = "polaris-inbound-cluster" 71 ) 72 73 const ( 74 // LocalRateLimitStage envoy local ratelimit stage 75 LocalRateLimitStage = 0 76 // DistributedRateLimitStage envoy remote ratelimit stage 77 DistributedRateLimitStage = 1 78 ) 79 80 var ( 81 TrafficBoundRoute = map[corev3.TrafficDirection]string{ 82 corev3.TrafficDirection_INBOUND: InBoundRouteConfigName, 83 corev3.TrafficDirection_OUTBOUND: OutBoundRouteConfigName, 84 } 85 ) 86 87 func MakeServiceGatewayDomains() []string { 88 return []string{"*"} 89 } 90 91 func FilterInboundRouterRule(svc *ServiceInfo) []*traffic_manage.SubRuleRouting { 92 ret := make([]*traffic_manage.SubRuleRouting, 0, 16) 93 for _, rule := range svc.Routing.GetRules() { 94 if rule.GetRoutingPolicy() != traffic_manage.RoutingPolicy_RulePolicy { 95 continue 96 } 97 routerRule := &traffic_manage.RuleRoutingConfig{} 98 if err := ptypes.UnmarshalAny(rule.RoutingConfig, routerRule); err != nil { 99 continue 100 } 101 102 for i, subRule := range routerRule.Rules { 103 var match bool 104 for _, dest := range subRule.GetDestinations() { 105 if svc.MatchService(dest.GetNamespace(), dest.GetService()) { 106 match = true 107 break 108 } 109 } 110 if match { 111 ret = append(ret, routerRule.Rules[i]) 112 } 113 } 114 } 115 return ret 116 } 117 118 func BuildSidecarRouteMatch(routeMatch *route.RouteMatch, source *traffic_manage.SourceService) { 119 for i := range source.GetArguments() { 120 argument := source.GetArguments()[i] 121 if argument.Type == traffic_manage.SourceMatch_PATH { 122 if argument.Value.Type == apimodel.MatchString_EXACT { 123 routeMatch.PathSpecifier = &route.RouteMatch_Path{ 124 Path: argument.GetValue().GetValue().GetValue()} 125 } else if argument.Value.Type == apimodel.MatchString_REGEX { 126 routeMatch.PathSpecifier = &route.RouteMatch_SafeRegex{SafeRegex: &v32.RegexMatcher{ 127 Regex: argument.GetValue().GetValue().GetValue()}} 128 } 129 } 130 } 131 BuildCommonRouteMatch(routeMatch, source) 132 } 133 134 func BuildCommonRouteMatch(routeMatch *route.RouteMatch, source *traffic_manage.SourceService) { 135 for i := range source.GetArguments() { 136 argument := source.GetArguments()[i] 137 switch argument.Type { 138 case traffic_manage.SourceMatch_HEADER: 139 headerSubName := argument.Key 140 var headerMatch *route.HeaderMatcher 141 if argument.Value.Type == apimodel.MatchString_EXACT { 142 headerMatch = &route.HeaderMatcher{ 143 Name: headerSubName, 144 HeaderMatchSpecifier: &route.HeaderMatcher_StringMatch{ 145 StringMatch: &v32.StringMatcher{ 146 MatchPattern: &v32.StringMatcher_Exact{ 147 Exact: argument.GetValue().GetValue().GetValue()}}, 148 }, 149 } 150 } 151 if argument.Value.Type == apimodel.MatchString_NOT_EQUALS { 152 headerMatch = &route.HeaderMatcher{ 153 Name: headerSubName, 154 HeaderMatchSpecifier: &route.HeaderMatcher_StringMatch{ 155 StringMatch: &v32.StringMatcher{ 156 MatchPattern: &v32.StringMatcher_Exact{ 157 Exact: argument.GetValue().GetValue().GetValue()}}, 158 }, 159 InvertMatch: true, 160 } 161 } 162 if argument.Value.Type == apimodel.MatchString_REGEX { 163 headerMatch = &route.HeaderMatcher{ 164 Name: headerSubName, 165 HeaderMatchSpecifier: &route.HeaderMatcher_StringMatch{ 166 StringMatch: &v32.StringMatcher{MatchPattern: &v32.StringMatcher_SafeRegex{ 167 SafeRegex: &v32.RegexMatcher{ 168 EngineType: &v32.RegexMatcher_GoogleRe2{ 169 GoogleRe2: &v32.RegexMatcher_GoogleRE2{}}, 170 Regex: argument.GetValue().GetValue().GetValue()}}}, 171 }, 172 } 173 } 174 if headerMatch != nil { 175 routeMatch.Headers = append(routeMatch.Headers, headerMatch) 176 } 177 case traffic_manage.SourceMatch_QUERY: 178 querySubName := argument.Key 179 var queryMatcher *route.QueryParameterMatcher 180 if argument.Value.Type == apimodel.MatchString_EXACT { 181 queryMatcher = &route.QueryParameterMatcher{ 182 Name: querySubName, 183 QueryParameterMatchSpecifier: &route.QueryParameterMatcher_StringMatch{ 184 StringMatch: &v32.StringMatcher{ 185 MatchPattern: &v32.StringMatcher_Exact{ 186 Exact: argument.GetValue().GetValue().GetValue()}}, 187 }, 188 } 189 } 190 if argument.Value.Type == apimodel.MatchString_REGEX { 191 queryMatcher = &route.QueryParameterMatcher{ 192 Name: querySubName, 193 QueryParameterMatchSpecifier: &route.QueryParameterMatcher_StringMatch{ 194 StringMatch: &v32.StringMatcher{ 195 MatchPattern: &v32.StringMatcher_SafeRegex{SafeRegex: &v32.RegexMatcher{ 196 EngineType: &v32.RegexMatcher_GoogleRe2{ 197 GoogleRe2: &v32.RegexMatcher_GoogleRE2{}}, 198 Regex: argument.GetValue().GetValue().GetValue(), 199 }}}, 200 }, 201 } 202 } 203 if queryMatcher != nil { 204 routeMatch.QueryParameters = append(routeMatch.QueryParameters, queryMatcher) 205 } 206 } 207 } 208 } 209 210 func BuildWeightClustersV2(trafficDirection corev3.TrafficDirection, 211 destinations []*traffic_manage.DestinationGroup) *route.WeightedCluster { 212 var ( 213 weightedClusters []*route.WeightedCluster_ClusterWeight 214 totalWeight uint32 215 ) 216 217 // 使用 destinations 生成 weightedClusters。makeClusters() 也使用这个字段生成对应的 subset 218 for _, destination := range destinations { 219 if destination.GetWeight() == 0 { 220 continue 221 } 222 fields := make(map[string]*_struct.Value) 223 for k, v := range destination.GetLabels() { 224 if k == utils.MatchAll && v.GetValue().GetValue() == utils.MatchAll { 225 // 重置 cluster 的匹配规则 226 fields = make(map[string]*_struct.Value) 227 break 228 } 229 fields[k] = &_struct.Value{ 230 Kind: &_struct.Value_StringValue{ 231 StringValue: v.Value.Value, 232 }, 233 } 234 } 235 weightCluster := &route.WeightedCluster_ClusterWeight{ 236 Name: MakeServiceName(model.ServiceKey{ 237 Namespace: destination.Namespace, 238 Name: destination.Service, 239 }, trafficDirection), 240 Weight: utils.NewUInt32Value(destination.GetWeight()), 241 MetadataMatch: &core.Metadata{ 242 FilterMetadata: map[string]*_struct.Struct{ 243 "envoy.lb": { 244 Fields: fields, 245 }, 246 }, 247 }, 248 } 249 if len(fields) == 0 { 250 weightCluster.MetadataMatch = nil 251 } 252 weightedClusters = append(weightedClusters, weightCluster) 253 totalWeight += destination.Weight 254 } 255 256 return &route.WeightedCluster{ 257 TotalWeight: &wrappers.UInt32Value{Value: totalWeight}, 258 Clusters: weightedClusters, 259 } 260 } 261 262 func BuildRateLimitConf(prefix string) *lrl.LocalRateLimit { 263 rateLimitConf := &lrl.LocalRateLimit{ 264 StatPrefix: prefix, 265 // 默认全局限流没限制,由于 envoy 这里必须设置一个 TokenBucket,因此这里只能设置一个认为不可能达到的一个 TPS 进行实现不限流 266 // TPS = 4294967295/s 267 TokenBucket: &typev3.TokenBucket{ 268 MaxTokens: math.MaxUint32, 269 TokensPerFill: wrapperspb.UInt32(math.MaxUint32), 270 FillInterval: durationpb.New(time.Second), 271 }, 272 FilterEnabled: &core.RuntimeFractionalPercent{ 273 RuntimeKey: prefix + "_local_rate_limit_enabled", 274 DefaultValue: &envoy_type_v3.FractionalPercent{ 275 Numerator: uint32(100), 276 Denominator: envoy_type_v3.FractionalPercent_HUNDRED, 277 }, 278 }, 279 FilterEnforced: &core.RuntimeFractionalPercent{ 280 RuntimeKey: prefix + "_local_rate_limit_enforced", 281 DefaultValue: &envoy_type_v3.FractionalPercent{ 282 Numerator: uint32(100), 283 Denominator: envoy_type_v3.FractionalPercent_HUNDRED, 284 }, 285 }, 286 ResponseHeadersToAdd: []*core.HeaderValueOption{ 287 { 288 Header: &core.HeaderValue{ 289 Key: "x-local-rate-limit", 290 Value: "true", 291 }, 292 Append: wrapperspb.Bool(false), 293 }, 294 }, 295 // the token bucket must shared across all worker threads 296 LocalRateLimitPerDownstreamConnection: false, 297 } 298 return rateLimitConf 299 } 300 301 func BuildRateLimitDescriptors(rule *traffic_manage.Rule) ([]*route.RateLimit_Action, 302 []*ratelimitv32.LocalRateLimitDescriptor) { 303 actions := make([]*route.RateLimit_Action, 0, 8) 304 descriptors := make([]*ratelimitv32.LocalRateLimitDescriptor, 0, 8) 305 306 entries := make([]*envoy_extensions_common_ratelimit_v3.RateLimitDescriptor_Entry, 0, len(rule.Labels)) 307 308 methodMatchType := rule.GetMethod().GetType() 309 methodName := rule.GetMethod().GetValue().GetValue() 310 if methodName == "" { 311 methodName = "/" 312 methodMatchType = MatchString_Prefix 313 } 314 actions = append(actions, &route.RateLimit_Action{ 315 ActionSpecifier: &route.RateLimit_Action_HeaderValueMatch_{ 316 HeaderValueMatch: BuildRateLimitActionHeaderValueMatch(":path", &apitraffic.MatchArgument{ 317 Key: ":path", 318 Value: &apimodel.MatchString{ 319 Type: methodMatchType, 320 Value: wrapperspb.String(methodName), 321 ValueType: apimodel.MatchString_TEXT, 322 }, 323 }), 324 }, 325 }) 326 entries = append(entries, &ratelimitv32.RateLimitDescriptor_Entry{ 327 Key: ":path", 328 Value: methodName, 329 }) 330 arguments := rule.GetArguments() 331 332 for i := range arguments { 333 arg := arguments[i] 334 descriptorKey := strings.ToLower(arg.GetType().String()) + "." + arg.Key 335 switch arg.Type { 336 case apitraffic.MatchArgument_HEADER: 337 headerValueMatch := BuildRateLimitActionHeaderValueMatch(descriptorKey, arg) 338 actions = append(actions, &route.RateLimit_Action{ 339 ActionSpecifier: &route.RateLimit_Action_HeaderValueMatch_{ 340 HeaderValueMatch: headerValueMatch, 341 }, 342 }) 343 entries = append(entries, &ratelimitv32.RateLimitDescriptor_Entry{ 344 Key: descriptorKey, 345 Value: arg.GetValue().GetValue().GetValue(), 346 }) 347 case apitraffic.MatchArgument_QUERY: 348 queryParameterValueMatch := BuildRateLimitActionQueryParameterValueMatch(descriptorKey, arg.Value) 349 actions = append(actions, &route.RateLimit_Action{ 350 ActionSpecifier: &route.RateLimit_Action_QueryParameterValueMatch_{ 351 QueryParameterValueMatch: queryParameterValueMatch, 352 }, 353 }) 354 entries = append(entries, &ratelimitv32.RateLimitDescriptor_Entry{ 355 Key: descriptorKey, 356 Value: arg.GetValue().GetValue().GetValue(), 357 }) 358 case apitraffic.MatchArgument_METHOD: 359 actions = append(actions, &route.RateLimit_Action{ 360 ActionSpecifier: &route.RateLimit_Action_RequestHeaders_{ 361 RequestHeaders: &route.RateLimit_Action_RequestHeaders{ 362 HeaderName: ":method", 363 DescriptorKey: descriptorKey, 364 }, 365 }, 366 }) 367 entries = append(entries, &envoy_extensions_common_ratelimit_v3.RateLimitDescriptor_Entry{ 368 Key: descriptorKey, 369 Value: arg.GetValue().GetValue().GetValue(), 370 }) 371 case apitraffic.MatchArgument_CALLER_IP: 372 actions = append(actions, &route.RateLimit_Action{ 373 ActionSpecifier: &route.RateLimit_Action_RemoteAddress_{ 374 RemoteAddress: &route.RateLimit_Action_RemoteAddress{}, 375 }, 376 }) 377 } 378 } 379 380 for _, amount := range rule.Amounts { 381 descriptor := &envoy_extensions_common_ratelimit_v3.LocalRateLimitDescriptor{ 382 TokenBucket: &envoy_type_v3.TokenBucket{ 383 MaxTokens: amount.GetMaxAmount().GetValue(), 384 TokensPerFill: wrapperspb.UInt32(amount.GetMaxAmount().GetValue()), 385 FillInterval: amount.GetValidDuration(), 386 }, 387 } 388 descriptor.Entries = entries 389 descriptors = append(descriptors, descriptor) 390 } 391 return actions, descriptors 392 } 393 394 func BuildRateLimitActionQueryParameterValueMatch(key string, 395 value *apimodel.MatchString) *route.RateLimit_Action_QueryParameterValueMatch { 396 queryParameterValueMatch := &route.RateLimit_Action_QueryParameterValueMatch{ 397 DescriptorKey: key, 398 DescriptorValue: value.GetValue().GetValue(), 399 ExpectMatch: wrapperspb.Bool(true), 400 QueryParameters: []*route.QueryParameterMatcher{}, 401 } 402 switch value.GetType() { 403 case apimodel.MatchString_EXACT: 404 queryParameterValueMatch.QueryParameters = []*route.QueryParameterMatcher{ 405 { 406 Name: key, 407 QueryParameterMatchSpecifier: &route.QueryParameterMatcher_StringMatch{ 408 StringMatch: &v32.StringMatcher{ 409 MatchPattern: &v32.StringMatcher_Exact{ 410 Exact: value.GetValue().GetValue(), 411 }, 412 }, 413 }, 414 }, 415 } 416 case apimodel.MatchString_REGEX: 417 queryParameterValueMatch.QueryParameters = []*route.QueryParameterMatcher{ 418 { 419 Name: key, 420 QueryParameterMatchSpecifier: &route.QueryParameterMatcher_StringMatch{ 421 StringMatch: &v32.StringMatcher{ 422 MatchPattern: &v32.StringMatcher_SafeRegex{ 423 SafeRegex: &v32.RegexMatcher{ 424 EngineType: &v32.RegexMatcher_GoogleRe2{}, 425 Regex: value.GetValue().GetValue(), 426 }, 427 }, 428 }, 429 }, 430 }, 431 } 432 } 433 434 return queryParameterValueMatch 435 } 436 437 func BuildRateLimitActionHeaderValueMatch(key string, argument *apitraffic.MatchArgument) *route.RateLimit_Action_HeaderValueMatch { 438 headerValueMatch := &route.RateLimit_Action_HeaderValueMatch{ 439 DescriptorKey: key, 440 DescriptorValue: argument.GetValue().GetValue().GetValue(), 441 Headers: []*route.HeaderMatcher{}, 442 } 443 switch argument.GetValue().GetType() { 444 case apimodel.MatchString_EXACT, apimodel.MatchString_NOT_EQUALS: 445 headerValueMatch.Headers = []*route.HeaderMatcher{ 446 { 447 Name: argument.GetKey(), 448 InvertMatch: argument.GetValue().GetType() == apimodel.MatchString_NOT_EQUALS, 449 HeaderMatchSpecifier: &route.HeaderMatcher_StringMatch{ 450 StringMatch: &v32.StringMatcher{ 451 MatchPattern: &v32.StringMatcher_Exact{ 452 Exact: argument.GetValue().GetValue().GetValue(), 453 }, 454 }, 455 }, 456 }, 457 } 458 case apimodel.MatchString_REGEX: 459 headerValueMatch.Headers = []*route.HeaderMatcher{ 460 { 461 Name: argument.GetKey(), 462 HeaderMatchSpecifier: &route.HeaderMatcher_SafeRegexMatch{ 463 SafeRegexMatch: &v32.RegexMatcher{ 464 EngineType: &v32.RegexMatcher_GoogleRe2{}, 465 Regex: argument.GetValue().GetValue().GetValue(), 466 }, 467 }, 468 }, 469 } 470 case MatchString_Prefix: 471 // 专门用于 prefix 472 headerValueMatch.Headers = []*route.HeaderMatcher{ 473 { 474 Name: argument.GetKey(), 475 HeaderMatchSpecifier: &route.HeaderMatcher_StringMatch{ 476 StringMatch: &v32.StringMatcher{ 477 MatchPattern: &v32.StringMatcher_Prefix{ 478 Prefix: argument.GetValue().GetValue().GetValue(), 479 }, 480 }, 481 }, 482 }, 483 } 484 } 485 return headerValueMatch 486 } 487 488 // 默认路由 489 func MakeDefaultRoute(trafficDirection corev3.TrafficDirection, svcKey model.ServiceKey) *route.Route { 490 return &route.Route{ 491 Match: &route.RouteMatch{ 492 PathSpecifier: &route.RouteMatch_Prefix{ 493 Prefix: "/", 494 }, 495 }, 496 Action: &route.Route_Route{ 497 Route: &route.RouteAction{ 498 ClusterSpecifier: &route.RouteAction_Cluster{ 499 Cluster: MakeServiceName(svcKey, trafficDirection), 500 }, 501 }, 502 }, 503 } 504 } 505 506 func GenerateServiceDomains(serviceInfo *ServiceInfo) []string { 507 // k8s dns 可解析的服务名 508 domain := serviceInfo.Name + "." + serviceInfo.Namespace 509 domains := []string{serviceInfo.Name, domain, 510 domain + K8sDnsResolveSuffixSvc, 511 domain + K8sDnsResolveSuffixSvcCluster, 512 domain + K8sDnsResolveSuffixSvcClusterLocal} 513 514 resDomains := domains 515 // 上面各种服务名加服务端口 516 ports := serviceInfo.Ports 517 for _, port := range ports { 518 // 如果是数字,则为每个域名产生一个带端口的域名 519 for _, s := range domains { 520 resDomains = append(resDomains, s+":"+strconv.FormatUint(uint64(port.Port), 10)) 521 } 522 } 523 return resDomains 524 } 525 526 func BuildAllowAnyVHost() *route.VirtualHost { 527 return &route.VirtualHost{ 528 Name: "allow_any", 529 Domains: []string{"*"}, 530 Routes: []*route.Route{ 531 { 532 Match: &route.RouteMatch{ 533 PathSpecifier: &route.RouteMatch_Prefix{ 534 Prefix: "/", 535 }, 536 }, 537 Action: &route.Route_Route{ 538 Route: &route.RouteAction{ 539 ClusterSpecifier: &route.RouteAction_Cluster{ 540 Cluster: PassthroughClusterName, 541 }, 542 }, 543 }, 544 }, 545 }, 546 } 547 } 548 549 func MakeGatewayRoute(trafficDirection corev3.TrafficDirection, routeMatch *route.RouteMatch, 550 destinations []*traffic_manage.DestinationGroup) *route.Route { 551 sidecarRoute := &route.Route{ 552 Match: routeMatch, 553 Action: &route.Route_Route{ 554 Route: &route.RouteAction{ 555 ClusterSpecifier: &route.RouteAction_WeightedClusters{ 556 WeightedClusters: BuildWeightClustersV2(trafficDirection, destinations), 557 }, 558 }, 559 }, 560 } 561 return sidecarRoute 562 } 563 564 func MakeSidecarRoute(trafficDirection corev3.TrafficDirection, routeMatch *route.RouteMatch, 565 svcInfo *ServiceInfo, destinations []*traffic_manage.DestinationGroup) *route.Route { 566 weightClusters := BuildWeightClustersV2(trafficDirection, destinations) 567 for i := range weightClusters.Clusters { 568 weightClusters.Clusters[i].Name = MakeServiceName(svcInfo.ServiceKey, trafficDirection) 569 } 570 currentRoute := &route.Route{ 571 Match: routeMatch, 572 Action: &route.Route_Route{ 573 Route: &route.RouteAction{ 574 ClusterSpecifier: &route.RouteAction_WeightedClusters{ 575 WeightedClusters: weightClusters, 576 }, 577 }, 578 }, 579 } 580 return currentRoute 581 } 582 583 var PassthroughCluster = &cluster.Cluster{ 584 Name: PassthroughClusterName, 585 ConnectTimeout: durationpb.New(5 * time.Second), 586 ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_ORIGINAL_DST}, 587 LbPolicy: cluster.Cluster_CLUSTER_PROVIDED, 588 CircuitBreakers: &cluster.CircuitBreakers{ 589 Thresholds: []*cluster.CircuitBreakers_Thresholds{ 590 { 591 MaxConnections: &wrappers.UInt32Value{Value: math.MaxUint32}, 592 MaxPendingRequests: &wrappers.UInt32Value{Value: math.MaxUint32}, 593 MaxRequests: &wrappers.UInt32Value{Value: math.MaxUint32}, 594 MaxRetries: &wrappers.UInt32Value{Value: math.MaxUint32}, 595 }, 596 }, 597 }, 598 } 599 600 func MakeServiceName(svcKey model.ServiceKey, trafficDirection corev3.TrafficDirection) string { 601 return fmt.Sprintf("%s|%s|%s", corev3.TrafficDirection_name[int32(trafficDirection)], 602 svcKey.Namespace, svcKey.Name) 603 } 604 605 func MakeDefaultFilterChain() *listenerv3.FilterChain { 606 return &listenerv3.FilterChain{ 607 Name: "PassthroughFilterChain", 608 Filters: []*listenerv3.Filter{ 609 { 610 Name: wellknown.TCPProxy, 611 ConfigType: &listenerv3.Filter_TypedConfig{ 612 TypedConfig: MustNewAny(&tcp.TcpProxy{ 613 StatPrefix: PassthroughClusterName, 614 ClusterSpecifier: &tcp.TcpProxy_Cluster{ 615 Cluster: PassthroughClusterName, 616 }, 617 }), 618 }, 619 }, 620 }, 621 } 622 } 623 624 func MakeSidecarBoundHCM(svcKey model.ServiceKey, 625 trafficDirection corev3.TrafficDirection) *hcm.HttpConnectionManager { 626 routerFilter := &hcm.HttpFilter{ 627 Name: wellknown.Router, 628 ConfigType: &hcm.HttpFilter_TypedConfig{ 629 TypedConfig: MustNewAny(&routerv3.Router{}), 630 }, 631 } 632 633 hcmFilters := []*hcm.HttpFilter{routerFilter} 634 if trafficDirection == corev3.TrafficDirection_INBOUND { 635 hcmFilters = append([]*hcm.HttpFilter{ 636 { 637 Name: "envoy.filters.http.local_ratelimit", 638 ConfigType: &hcm.HttpFilter_TypedConfig{ 639 TypedConfig: MustNewAny(&lrl.LocalRateLimit{ 640 StatPrefix: "http_local_rate_limiter", 641 Stage: LocalRateLimitStage, 642 }), 643 }, 644 }, 645 { 646 Name: "envoy.filters.http.ratelimit", 647 ConfigType: &hcm.HttpFilter_TypedConfig{ 648 TypedConfig: MustNewAny(&ratelimitfilter.RateLimit{ 649 Domain: fmt.Sprintf("%s.%s", svcKey.Name, svcKey.Namespace), 650 Stage: DistributedRateLimitStage, 651 RequestType: "external", 652 Timeout: durationpb.New(2 * time.Second), 653 RateLimitService: &ratelimitconfv3.RateLimitServiceConfig{ 654 GrpcService: &corev3.GrpcService{ 655 TargetSpecifier: &corev3.GrpcService_EnvoyGrpc_{ 656 EnvoyGrpc: &corev3.GrpcService_EnvoyGrpc{ 657 ClusterName: "polaris_ratelimit", 658 }, 659 }, 660 Timeout: durationpb.New(time.Second), 661 }, 662 TransportApiVersion: core.ApiVersion_V3, 663 }, 664 }), 665 }, 666 }, 667 }, hcmFilters...) 668 } 669 670 trafficDirectionName := corev3.TrafficDirection_name[int32(trafficDirection)] 671 manager := &hcm.HttpConnectionManager{ 672 CodecType: hcm.HttpConnectionManager_AUTO, 673 StatPrefix: trafficDirectionName + "_HTTP", 674 RouteSpecifier: routeSpecifier(trafficDirection), 675 AccessLog: accessLog(), 676 HttpFilters: hcmFilters, 677 HttpProtocolOptions: &core.Http1ProtocolOptions{AcceptHttp_10: true}, 678 } 679 return manager 680 } 681 682 func MakeGatewayBoundHCM() *hcm.HttpConnectionManager { 683 hcmFilters := []*hcm.HttpFilter{ 684 { 685 Name: "envoy.filters.http.local_ratelimit", 686 ConfigType: &hcm.HttpFilter_TypedConfig{ 687 TypedConfig: MustNewAny(&lrl.LocalRateLimit{ 688 StatPrefix: "http_local_rate_limiter", 689 }), 690 }, 691 }, 692 { 693 Name: wellknown.Router, 694 ConfigType: &hcm.HttpFilter_TypedConfig{ 695 TypedConfig: MustNewAny(&routerv3.Router{}), 696 }, 697 }, 698 } 699 trafficDirectionName := corev3.TrafficDirection_name[int32(corev3.TrafficDirection_INBOUND)] 700 manager := &hcm.HttpConnectionManager{ 701 CodecType: hcm.HttpConnectionManager_AUTO, 702 StatPrefix: trafficDirectionName + "_HTTP", 703 RouteSpecifier: routeSpecifier(corev3.TrafficDirection_OUTBOUND), 704 AccessLog: accessLog(), 705 HttpFilters: hcmFilters, 706 HttpProtocolOptions: &core.Http1ProtocolOptions{AcceptHttp_10: true}, 707 } 708 return manager 709 } 710 711 func routeSpecifier(trafficDirection corev3.TrafficDirection) *hcm.HttpConnectionManager_Rds { 712 return &hcm.HttpConnectionManager_Rds{ 713 Rds: &hcm.Rds{ 714 ConfigSource: &core.ConfigSource{ 715 ResourceApiVersion: resourcev3.DefaultAPIVersion, 716 ConfigSourceSpecifier: &core.ConfigSource_Ads{ 717 Ads: &core.AggregatedConfigSource{}, 718 }, 719 }, 720 RouteConfigName: TrafficBoundRoute[trafficDirection], 721 }, 722 } 723 } 724 725 func accessLog() []*accesslog.AccessLog { 726 return []*accesslog.AccessLog{ 727 { 728 Name: wellknown.FileAccessLog, 729 ConfigType: &accesslog.AccessLog_TypedConfig{ 730 TypedConfig: MustNewAny(&filev3.FileAccessLog{ 731 Path: "/dev/stdout", 732 }), 733 }, 734 }, 735 } 736 } 737 738 func MustNewAny(src proto.Message) *anypb.Any { 739 a, _ := anypb.New(src) 740 return a 741 } 742 743 func MakeGatewayLocalRateLimit(rateLimitCache types.RateLimitCache, pathSpecifier string, 744 svcKey model.ServiceKey) ([]*route.RateLimit, map[string]*anypb.Any, error) { 745 conf, _ := rateLimitCache.GetRateLimitRules(svcKey) 746 if conf == nil { 747 return nil, nil, nil 748 } 749 confKey := fmt.Sprintf("INBOUND|GATEWAY|%s|%s|%s", svcKey.Namespace, svcKey.Name, pathSpecifier) 750 rateLimitConf := BuildRateLimitConf(confKey) 751 filters := make(map[string]*anypb.Any) 752 ratelimits := make([]*route.RateLimit, 0, len(conf)) 753 for _, c := range conf { 754 rule := c.Proto 755 if rule == nil { 756 continue 757 } 758 if rule.GetDisable().GetValue() { 759 continue 760 } 761 if rule.GetMethod().GetValue().GetValue() != pathSpecifier { 762 continue 763 } 764 actions, descriptors := BuildRateLimitDescriptors(rule) 765 rateLimitConf.Descriptors = descriptors 766 ratelimitRule := &route.RateLimit{Actions: actions} 767 switch rule.GetType() { 768 case apitraffic.Rule_LOCAL: 769 ratelimitRule.Stage = wrapperspb.UInt32(LocalRateLimitStage) 770 case apitraffic.Rule_GLOBAL: 771 ratelimitRule.Stage = wrapperspb.UInt32(DistributedRateLimitStage) 772 } 773 ratelimits = append(ratelimits, ratelimitRule) 774 } 775 if len(ratelimits) == 0 { 776 return nil, nil, nil 777 } 778 filters["envoy.filters.http.local_ratelimit"] = MustNewAny(rateLimitConf) 779 return ratelimits, filters, nil 780 } 781 782 func MakeSidecarLocalRateLimit(rateLimitCache types.RateLimitCache, 783 svcKey model.ServiceKey) ([]*route.RateLimit, map[string]*anypb.Any, error) { 784 conf, _ := rateLimitCache.GetRateLimitRules(svcKey) 785 if conf == nil { 786 return nil, nil, nil 787 } 788 confKey := fmt.Sprintf("INBOUND|SIDECAR|%s|%s", svcKey.Namespace, svcKey.Name) 789 rateLimitConf := BuildRateLimitConf(confKey) 790 filters := make(map[string]*anypb.Any) 791 ratelimits := make([]*route.RateLimit, 0, len(conf)) 792 for _, c := range conf { 793 rule := c.Proto 794 if rule == nil { 795 continue 796 } 797 if rule.GetDisable().GetValue() { 798 continue 799 } 800 actions, descriptors := BuildRateLimitDescriptors(rule) 801 rateLimitConf.Descriptors = descriptors 802 ratelimitRule := &route.RateLimit{Actions: actions} 803 switch rule.GetType() { 804 case apitraffic.Rule_LOCAL: 805 ratelimitRule.Stage = wrapperspb.UInt32(LocalRateLimitStage) 806 case apitraffic.Rule_GLOBAL: 807 ratelimitRule.Stage = wrapperspb.UInt32(DistributedRateLimitStage) 808 } 809 ratelimits = append(ratelimits, ratelimitRule) 810 } 811 filters["envoy.filters.http.local_ratelimit"] = MustNewAny(rateLimitConf) 812 return ratelimits, filters, nil 813 } 814 815 // Translate the circuit breaker configuration of Polaris into OutlierDetection 816 func MakeOutlierDetection(serviceInfo *ServiceInfo) *cluster.OutlierDetection { 817 log.Infof("XDS][Sidecar] makeOutlierDetection service: %v enter", serviceInfo.Name) 818 circuitBreaker := serviceInfo.CircuitBreaker 819 if circuitBreaker == nil || len(circuitBreaker.Rules) == 0 { 820 return nil 821 } 822 var rule *apifault.CircuitBreakerRule 823 for _, item := range circuitBreaker.Rules { 824 log.Infof("[XDS][Sidecar] makeOutlierDetection rule: %+v", item) 825 if item.Level == apifault.Level_INSTANCE { 826 rule = item 827 break 828 } 829 } 830 // not config or close circuit breaker 831 if rule == nil || len(rule.TriggerCondition) == 0 || !rule.Enable { 832 return nil 833 } 834 triggerCondtion := rule.TriggerCondition[0] 835 outlierDetection := &cluster.OutlierDetection{} 836 outlierDetection.Interval = durationpb.New(time.Duration(triggerCondtion.GetInterval()) * time.Second) 837 outlierDetection.Consecutive_5Xx = &wrappers.UInt32Value{ 838 Value: triggerCondtion.GetErrorCount()} 839 outlierDetection.FailurePercentageThreshold = &wrappers.UInt32Value{ 840 Value: triggerCondtion.GetErrorPercent()} 841 outlierDetection.FailurePercentageRequestVolume = &wrappers.UInt32Value{ 842 Value: triggerCondtion.GetMinimumRequest()} 843 if rule.RecoverCondition != nil { 844 outlierDetection.BaseEjectionTime = 845 durationpb.New(time.Duration(rule.GetRecoverCondition().GetSleepWindow()) * time.Second) 846 } 847 848 return outlierDetection 849 } 850 851 // Translate the FaultDetector configuration of Polaris into HealthCheck 852 func MakeHealthCheck(serviceInfo *ServiceInfo) []*core.HealthCheck { 853 log.Infof("XDS][Sidecar] makeHealthCheck service: %v enter", serviceInfo.Name) 854 if serviceInfo.FaultDetect == nil || len(serviceInfo.FaultDetect.Rules) == 0 { 855 return nil 856 } 857 var healthChecks []*core.HealthCheck 858 for _, rule := range serviceInfo.FaultDetect.Rules { 859 log.Infof("[XDS][Sidecar] makeHealthCheck name: %v, rule: %+v", rule.Name, rule) 860 healthCheck := &core.HealthCheck{ 861 Timeout: durationpb.New(time.Duration(rule.GetTimeout()) * time.Second), 862 Interval: durationpb.New(time.Duration(rule.GetInterval()) * time.Second), 863 UnhealthyThreshold: &wrappers.UInt32Value{Value: 3}, 864 HealthyThreshold: &wrappers.UInt32Value{Value: 1}, 865 } 866 if rule.GetProtocol() == apifault.FaultDetectRule_HTTP { 867 config := rule.GetHttpConfig() 868 if config == nil { 869 continue 870 } 871 var headers []*core.HeaderValueOption 872 for _, item := range config.GetHeaders() { 873 header := core.HeaderValueOption{ 874 Header: &core.HeaderValue{ 875 Key: item.Key, 876 Value: item.Value, 877 }, 878 } 879 headers = append(headers, &header) 880 } 881 882 httpHealthCheck := &core.HealthCheck_HttpHealthCheck{ 883 Path: config.Url, 884 Method: core.RequestMethod(core.RequestMethod_value[config.Method]), 885 RequestHeadersToAdd: headers, 886 } 887 healthCheck.HealthChecker = &core.HealthCheck_HttpHealthCheck_{HttpHealthCheck: httpHealthCheck} 888 healthChecks = append(healthChecks, healthCheck) 889 } else if rule.GetProtocol() == apifault.FaultDetectRule_TCP { 890 config := rule.GetTcpConfig() 891 if config == nil { 892 continue 893 } 894 var receives []*core.HealthCheck_Payload 895 for _, item := range config.GetReceive() { 896 receives = append(receives, &core.HealthCheck_Payload{ 897 Payload: &core.HealthCheck_Payload_Text{Text: hex.EncodeToString([]byte(item))}, 898 }) 899 } 900 tcpHealthCheck := &core.HealthCheck_TcpHealthCheck{ 901 Send: &core.HealthCheck_Payload{ 902 Payload: &core.HealthCheck_Payload_Text{Text: hex.EncodeToString([]byte(config.Send))}, 903 }, 904 Receive: receives, 905 } 906 healthCheck.HealthChecker = &core.HealthCheck_TcpHealthCheck_{TcpHealthCheck: tcpHealthCheck} 907 healthChecks = append(healthChecks, healthCheck) 908 } 909 } 910 return healthChecks 911 } 912 913 func MakeLbSubsetConfig(serviceInfo *ServiceInfo) *cluster.Cluster_LbSubsetConfig { 914 rules := FilterInboundRouterRule(serviceInfo) 915 if len(rules) == 0 { 916 return nil 917 } 918 919 lbSubsetConfig := &cluster.Cluster_LbSubsetConfig{} 920 var subsetSelectors []*cluster.Cluster_LbSubsetConfig_LbSubsetSelector 921 lbSubsetConfig.FallbackPolicy = cluster.Cluster_LbSubsetConfig_ANY_ENDPOINT 922 923 for _, rule := range rules { 924 // 对每一个 destination 产生一个 subset 925 for _, destination := range rule.GetDestinations() { 926 var keys []string 927 for s := range destination.GetLabels() { 928 keys = append(keys, s) 929 } 930 subsetSelectors = append(subsetSelectors, &cluster.Cluster_LbSubsetConfig_LbSubsetSelector{ 931 Keys: keys, 932 FallbackPolicy: cluster.Cluster_LbSubsetConfig_LbSubsetSelector_NO_FALLBACK, 933 }) 934 } 935 } 936 937 lbSubsetConfig.SubsetSelectors = subsetSelectors 938 return lbSubsetConfig 939 } 940 941 func GenEndpointMetaFromPolarisIns(ins *apiservice.Instance) *core.Metadata { 942 meta := &core.Metadata{} 943 fields := make(map[string]*_struct.Value) 944 for k, v := range ins.Metadata { 945 fields[k] = &_struct.Value{ 946 Kind: &_struct.Value_StringValue{ 947 StringValue: v, 948 }, 949 } 950 } 951 952 meta.FilterMetadata = make(map[string]*_struct.Struct) 953 meta.FilterMetadata["envoy.lb"] = &_struct.Struct{ 954 Fields: fields, 955 } 956 if ins.Metadata != nil && ins.Metadata[TLSModeTag] != "" { 957 meta.FilterMetadata["envoy.transport_socket_match"] = MTLSTransportSocketMatch 958 } 959 return meta 960 } 961 962 func IsNormalEndpoint(ins *apiservice.Instance) bool { 963 if ins.GetIsolate().GetValue() { 964 return false 965 } 966 if ins.GetWeight().GetValue() == 0 { 967 return false 968 } 969 return true 970 } 971 972 func FormatEndpointHealth(ins *apiservice.Instance) core.HealthStatus { 973 if ins.GetHealthy().GetValue() { 974 return core.HealthStatus_HEALTHY 975 } 976 return core.HealthStatus_UNHEALTHY 977 }