1
17
18 package xdsresource
19
20 import (
21 "encoding/json"
22 "errors"
23 "fmt"
24 "net"
25 "strconv"
26 "strings"
27 "time"
28
29 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
30 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
31 v3aggregateclusterpb "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3"
32 v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
33
34 "google.golang.org/grpc/internal/envconfig"
35 "google.golang.org/grpc/internal/pretty"
36 iserviceconfig "google.golang.org/grpc/internal/serviceconfig"
37 "google.golang.org/grpc/internal/xds/bootstrap"
38 "google.golang.org/grpc/internal/xds/matcher"
39 "google.golang.org/grpc/xds/internal/xdsclient/xdslbregistry"
40 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
41 "google.golang.org/protobuf/proto"
42 "google.golang.org/protobuf/types/known/anypb"
43 "google.golang.org/protobuf/types/known/structpb"
44 )
45
46
47
48 var ValidateClusterAndConstructClusterUpdateForTesting = validateClusterAndConstructClusterUpdate
49
50
51
52 const transportSocketName = "envoy.transport_sockets.tls"
53
54 func unmarshalClusterResource(r *anypb.Any, serverCfg *bootstrap.ServerConfig) (string, ClusterUpdate, error) {
55 r, err := UnwrapResource(r)
56 if err != nil {
57 return "", ClusterUpdate{}, fmt.Errorf("failed to unwrap resource: %v", err)
58 }
59
60 if !IsClusterResource(r.GetTypeUrl()) {
61 return "", ClusterUpdate{}, fmt.Errorf("unexpected resource type: %q ", r.GetTypeUrl())
62 }
63
64 cluster := &v3clusterpb.Cluster{}
65 if err := proto.Unmarshal(r.GetValue(), cluster); err != nil {
66 return "", ClusterUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err)
67 }
68 cu, err := validateClusterAndConstructClusterUpdate(cluster, serverCfg)
69 if err != nil {
70 return cluster.GetName(), ClusterUpdate{}, err
71 }
72 cu.Raw = r
73
74 return cluster.GetName(), cu, nil
75 }
76
77 const (
78 defaultRingHashMinSize = 1024
79 defaultRingHashMaxSize = 8 * 1024 * 1024
80 ringHashSizeUpperBound = 8 * 1024 * 1024
81
82 defaultLeastRequestChoiceCount = 2
83 )
84
85 func validateClusterAndConstructClusterUpdate(cluster *v3clusterpb.Cluster, serverCfg *bootstrap.ServerConfig) (ClusterUpdate, error) {
86 telemetryLabels := make(map[string]string)
87 if fmd := cluster.GetMetadata().GetFilterMetadata(); fmd != nil {
88 if val, ok := fmd["com.google.csm.telemetry_labels"]; ok {
89 if fields := val.GetFields(); fields != nil {
90 if val, ok := fields["service_name"]; ok {
91 if _, ok := val.GetKind().(*structpb.Value_StringValue); ok {
92 telemetryLabels["service_name"] = val.GetStringValue()
93 }
94 }
95 if val, ok := fields["service_namespace"]; ok {
96 if _, ok := val.GetKind().(*structpb.Value_StringValue); ok {
97 telemetryLabels["service_namespace"] = val.GetStringValue()
98 }
99 }
100 }
101 }
102 }
103
104 var lbPolicy json.RawMessage
105 var err error
106 switch cluster.GetLbPolicy() {
107 case v3clusterpb.Cluster_ROUND_ROBIN:
108 lbPolicy = []byte(`[{"xds_wrr_locality_experimental": {"childPolicy": [{"round_robin": {}}]}}]`)
109 case v3clusterpb.Cluster_RING_HASH:
110 rhc := cluster.GetRingHashLbConfig()
111 if rhc.GetHashFunction() != v3clusterpb.Cluster_RingHashLbConfig_XX_HASH {
112 return ClusterUpdate{}, fmt.Errorf("unsupported ring_hash hash function %v in response: %+v", rhc.GetHashFunction(), cluster)
113 }
114
115
116 var minSize, maxSize uint64 = defaultRingHashMinSize, defaultRingHashMaxSize
117 if min := rhc.GetMinimumRingSize(); min != nil {
118 minSize = min.GetValue()
119 }
120 if max := rhc.GetMaximumRingSize(); max != nil {
121 maxSize = max.GetValue()
122 }
123
124 rhLBCfg := []byte(fmt.Sprintf("{\"minRingSize\": %d, \"maxRingSize\": %d}", minSize, maxSize))
125 lbPolicy = []byte(fmt.Sprintf(`[{"ring_hash_experimental": %s}]`, rhLBCfg))
126 case v3clusterpb.Cluster_LEAST_REQUEST:
127 if !envconfig.LeastRequestLB {
128 return ClusterUpdate{}, fmt.Errorf("unexpected lbPolicy %v in response: %+v", cluster.GetLbPolicy(), cluster)
129 }
130
131
132
133
134 lr := cluster.GetLeastRequestLbConfig()
135 var choiceCount uint32 = defaultLeastRequestChoiceCount
136 if cc := lr.GetChoiceCount(); cc != nil {
137 choiceCount = cc.GetValue()
138 }
139
140 if choiceCount < 2 {
141 return ClusterUpdate{}, fmt.Errorf("Cluster_LeastRequestLbConfig.ChoiceCount must be >= 2, got: %v", choiceCount)
142 }
143
144 lrLBCfg := []byte(fmt.Sprintf("{\"choiceCount\": %d}", choiceCount))
145 lbPolicy = []byte(fmt.Sprintf(`[{"least_request_experimental": %s}]`, lrLBCfg))
146 default:
147 return ClusterUpdate{}, fmt.Errorf("unexpected lbPolicy %v in response: %+v", cluster.GetLbPolicy(), cluster)
148 }
149
150
151 var sc *SecurityConfig
152 if sc, err = securityConfigFromCluster(cluster); err != nil {
153 return ClusterUpdate{}, err
154 }
155
156
157
158 var od json.RawMessage
159 if od, err = outlierConfigFromCluster(cluster); err != nil {
160 return ClusterUpdate{}, err
161 }
162
163 if cluster.GetLoadBalancingPolicy() != nil {
164 lbPolicy, err = xdslbregistry.ConvertToServiceConfig(cluster.GetLoadBalancingPolicy(), 0)
165 if err != nil {
166 return ClusterUpdate{}, fmt.Errorf("error converting LoadBalancingPolicy %v in response: %+v: %v", cluster.GetLoadBalancingPolicy(), cluster, err)
167 }
168
169
170
171 bc := &iserviceconfig.BalancerConfig{}
172 if err := json.Unmarshal(lbPolicy, bc); err != nil {
173 return ClusterUpdate{}, fmt.Errorf("JSON generated from xDS LB policy registry: %s is invalid: %v", pretty.FormatJSON(lbPolicy), err)
174 }
175 }
176
177 ret := ClusterUpdate{
178 ClusterName: cluster.GetName(),
179 SecurityCfg: sc,
180 MaxRequests: circuitBreakersFromCluster(cluster),
181 LBPolicy: lbPolicy,
182 OutlierDetection: od,
183 TelemetryLabels: telemetryLabels,
184 }
185
186 if lrs := cluster.GetLrsServer(); lrs != nil {
187 if lrs.GetSelf() == nil {
188 return ClusterUpdate{}, fmt.Errorf("unsupported config_source_specifier %T in lrs_server field", lrs.ConfigSourceSpecifier)
189 }
190 ret.LRSServerConfig = serverCfg
191 }
192
193
194 switch {
195 case cluster.GetType() == v3clusterpb.Cluster_EDS:
196 if configsource := cluster.GetEdsClusterConfig().GetEdsConfig(); configsource.GetAds() == nil && configsource.GetSelf() == nil {
197 return ClusterUpdate{}, fmt.Errorf("CDS's EDS config source is not ADS or Self: %+v", cluster)
198 }
199 ret.ClusterType = ClusterTypeEDS
200 ret.EDSServiceName = cluster.GetEdsClusterConfig().GetServiceName()
201 if strings.HasPrefix(ret.ClusterName, "xdstp:") && ret.EDSServiceName == "" {
202 return ClusterUpdate{}, fmt.Errorf("CDS's EDS service name is not set with a new-style cluster name: %+v", cluster)
203 }
204 return ret, nil
205 case cluster.GetType() == v3clusterpb.Cluster_LOGICAL_DNS:
206 ret.ClusterType = ClusterTypeLogicalDNS
207 dnsHN, err := dnsHostNameFromCluster(cluster)
208 if err != nil {
209 return ClusterUpdate{}, err
210 }
211 ret.DNSHostName = dnsHN
212 return ret, nil
213 case cluster.GetClusterType() != nil && cluster.GetClusterType().Name == "envoy.clusters.aggregate":
214 clusters := &v3aggregateclusterpb.ClusterConfig{}
215 if err := proto.Unmarshal(cluster.GetClusterType().GetTypedConfig().GetValue(), clusters); err != nil {
216 return ClusterUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err)
217 }
218 if len(clusters.Clusters) == 0 {
219 return ClusterUpdate{}, fmt.Errorf("xds: aggregate cluster has empty clusters field in response: %+v", cluster)
220 }
221 ret.ClusterType = ClusterTypeAggregate
222 ret.PrioritizedClusterNames = clusters.Clusters
223 return ret, nil
224 default:
225 return ClusterUpdate{}, fmt.Errorf("unsupported cluster type (%v, %v) in response: %+v", cluster.GetType(), cluster.GetClusterType(), cluster)
226 }
227 }
228
229
230
231
232
233
234 func dnsHostNameFromCluster(cluster *v3clusterpb.Cluster) (string, error) {
235 loadAssignment := cluster.GetLoadAssignment()
236 if loadAssignment == nil {
237 return "", fmt.Errorf("load_assignment not present for LOGICAL_DNS cluster")
238 }
239 if len(loadAssignment.GetEndpoints()) != 1 {
240 return "", fmt.Errorf("load_assignment for LOGICAL_DNS cluster must have exactly one locality, got: %+v", loadAssignment)
241 }
242 endpoints := loadAssignment.GetEndpoints()[0].GetLbEndpoints()
243 if len(endpoints) != 1 {
244 return "", fmt.Errorf("locality for LOGICAL_DNS cluster must have exactly one endpoint, got: %+v", endpoints)
245 }
246 endpoint := endpoints[0].GetEndpoint()
247 if endpoint == nil {
248 return "", fmt.Errorf("endpoint for LOGICAL_DNS cluster not set")
249 }
250 socketAddr := endpoint.GetAddress().GetSocketAddress()
251 if socketAddr == nil {
252 return "", fmt.Errorf("socket address for endpoint for LOGICAL_DNS cluster not set")
253 }
254 if socketAddr.GetResolverName() != "" {
255 return "", fmt.Errorf("socket address for endpoint for LOGICAL_DNS cluster not set has unexpected custom resolver name: %v", socketAddr.GetResolverName())
256 }
257 host := socketAddr.GetAddress()
258 if host == "" {
259 return "", fmt.Errorf("host for endpoint for LOGICAL_DNS cluster not set")
260 }
261 port := socketAddr.GetPortValue()
262 if port == 0 {
263 return "", fmt.Errorf("port for endpoint for LOGICAL_DNS cluster not set")
264 }
265 return net.JoinHostPort(host, strconv.Itoa(int(port))), nil
266 }
267
268
269
270 func securityConfigFromCluster(cluster *v3clusterpb.Cluster) (*SecurityConfig, error) {
271 if tsm := cluster.GetTransportSocketMatches(); len(tsm) != 0 {
272 return nil, fmt.Errorf("unsupport transport_socket_matches field is non-empty: %+v", tsm)
273 }
274
275
276
277 ts := cluster.GetTransportSocket()
278 if ts == nil {
279 return nil, nil
280 }
281 if name := ts.GetName(); name != transportSocketName {
282 return nil, fmt.Errorf("transport_socket field has unexpected name: %s", name)
283 }
284 any := ts.GetTypedConfig()
285 if any == nil || any.TypeUrl != version.V3UpstreamTLSContextURL {
286 return nil, fmt.Errorf("transport_socket field has unexpected typeURL: %s", any.TypeUrl)
287 }
288 upstreamCtx := &v3tlspb.UpstreamTlsContext{}
289 if err := proto.Unmarshal(any.GetValue(), upstreamCtx); err != nil {
290 return nil, fmt.Errorf("failed to unmarshal UpstreamTlsContext in CDS response: %v", err)
291 }
292
293
294
295
296 if upstreamCtx.GetCommonTlsContext() == nil {
297 return nil, errors.New("UpstreamTlsContext in CDS response does not contain a CommonTlsContext")
298 }
299
300 return securityConfigFromCommonTLSContext(upstreamCtx.GetCommonTlsContext(), false)
301 }
302
303
304
305 func securityConfigFromCommonTLSContext(common *v3tlspb.CommonTlsContext, server bool) (*SecurityConfig, error) {
306 if common.GetTlsParams() != nil {
307 return nil, fmt.Errorf("unsupported tls_params field in CommonTlsContext message: %+v", common)
308 }
309 if common.GetCustomHandshaker() != nil {
310 return nil, fmt.Errorf("unsupported custom_handshaker field in CommonTlsContext message: %+v", common)
311 }
312
313
314
315
316 sc, _ := securityConfigFromCommonTLSContextUsingNewFields(common, server)
317 if sc == nil || sc.Equal(&SecurityConfig{}) {
318 var err error
319 sc, err = securityConfigFromCommonTLSContextWithDeprecatedFields(common, server)
320 if err != nil {
321 return nil, err
322 }
323 }
324 if sc != nil {
325
326
327 if server {
328 if sc.IdentityInstanceName == "" {
329 return nil, errors.New("security configuration on the server-side does not contain identity certificate provider instance name")
330 }
331 } else {
332 if sc.RootInstanceName == "" {
333 return nil, errors.New("security configuration on the client-side does not contain root certificate provider instance name")
334 }
335 }
336 }
337 return sc, nil
338 }
339
340 func securityConfigFromCommonTLSContextWithDeprecatedFields(common *v3tlspb.CommonTlsContext, server bool) (*SecurityConfig, error) {
341
342
343
344
345 sc := &SecurityConfig{}
346 if identity := common.GetTlsCertificateCertificateProviderInstance(); identity != nil {
347 sc.IdentityInstanceName = identity.GetInstanceName()
348 sc.IdentityCertName = identity.GetCertificateName()
349 }
350
351
352
353
354
355
356
357
358
359
360 switch t := common.GetValidationContextType().(type) {
361 case *v3tlspb.CommonTlsContext_CombinedValidationContext:
362 combined := common.GetCombinedValidationContext()
363 var matchers []matcher.StringMatcher
364 if def := combined.GetDefaultValidationContext(); def != nil {
365 for _, m := range def.GetMatchSubjectAltNames() {
366 matcher, err := matcher.StringMatcherFromProto(m)
367 if err != nil {
368 return nil, err
369 }
370 matchers = append(matchers, matcher)
371 }
372 }
373 if server && len(matchers) != 0 {
374 return nil, fmt.Errorf("match_subject_alt_names field in validation context is not supported on the server: %v", common)
375 }
376 sc.SubjectAltNameMatchers = matchers
377 if pi := combined.GetValidationContextCertificateProviderInstance(); pi != nil {
378 sc.RootInstanceName = pi.GetInstanceName()
379 sc.RootCertName = pi.GetCertificateName()
380 }
381 case *v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance:
382 pi := common.GetValidationContextCertificateProviderInstance()
383 sc.RootInstanceName = pi.GetInstanceName()
384 sc.RootCertName = pi.GetCertificateName()
385 case nil:
386
387 default:
388 return nil, fmt.Errorf("validation context contains unexpected type: %T", t)
389 }
390 return sc, nil
391 }
392
393
394
395
396
397
398
399
400
401
402 func securityConfigFromCommonTLSContextUsingNewFields(common *v3tlspb.CommonTlsContext, server bool) (*SecurityConfig, error) {
403
404
405
406 sc := &SecurityConfig{}
407 identity := common.GetTlsCertificateProviderInstance()
408 if identity == nil && len(common.GetTlsCertificates()) != 0 {
409 return nil, fmt.Errorf("expected field tls_certificate_provider_instance is not set, while unsupported field tls_certificates is set in CommonTlsContext message: %+v", common)
410 }
411 if identity == nil && common.GetTlsCertificateSdsSecretConfigs() != nil {
412 return nil, fmt.Errorf("expected field tls_certificate_provider_instance is not set, while unsupported field tls_certificate_sds_secret_configs is set in CommonTlsContext message: %+v", common)
413 }
414 sc.IdentityInstanceName = identity.GetInstanceName()
415 sc.IdentityCertName = identity.GetCertificateName()
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440 var validationCtx *v3tlspb.CertificateValidationContext
441 switch typ := common.GetValidationContextType().(type) {
442 case *v3tlspb.CommonTlsContext_ValidationContext:
443 validationCtx = common.GetValidationContext()
444 case *v3tlspb.CommonTlsContext_CombinedValidationContext:
445 validationCtx = common.GetCombinedValidationContext().GetDefaultValidationContext()
446 case nil:
447
448 return sc, nil
449 default:
450 return nil, fmt.Errorf("validation context contains unexpected type: %T", typ)
451 }
452
453
454
455
456
457 if validationCtx.GetCaCertificateProviderInstance() == nil {
458 return nil, fmt.Errorf("expected field ca_certificate_provider_instance is missing in CommonTlsContext message: %+v", common)
459 }
460
461
462
463
464
465 switch {
466 case len(validationCtx.GetVerifyCertificateSpki()) != 0:
467 return nil, fmt.Errorf("unsupported verify_certificate_spki field in CommonTlsContext message: %+v", common)
468 case len(validationCtx.GetVerifyCertificateHash()) != 0:
469 return nil, fmt.Errorf("unsupported verify_certificate_hash field in CommonTlsContext message: %+v", common)
470 case validationCtx.GetRequireSignedCertificateTimestamp().GetValue():
471 return nil, fmt.Errorf("unsupported require_sugned_ceritificate_timestamp field in CommonTlsContext message: %+v", common)
472 case validationCtx.GetCrl() != nil:
473 return nil, fmt.Errorf("unsupported crl field in CommonTlsContext message: %+v", common)
474 case validationCtx.GetCustomValidatorConfig() != nil:
475 return nil, fmt.Errorf("unsupported custom_validator_config field in CommonTlsContext message: %+v", common)
476 }
477
478 if rootProvider := validationCtx.GetCaCertificateProviderInstance(); rootProvider != nil {
479 sc.RootInstanceName = rootProvider.GetInstanceName()
480 sc.RootCertName = rootProvider.GetCertificateName()
481 }
482 var matchers []matcher.StringMatcher
483 for _, m := range validationCtx.GetMatchSubjectAltNames() {
484 matcher, err := matcher.StringMatcherFromProto(m)
485 if err != nil {
486 return nil, err
487 }
488 matchers = append(matchers, matcher)
489 }
490 if server && len(matchers) != 0 {
491 return nil, fmt.Errorf("match_subject_alt_names field in validation context is not supported on the server: %v", common)
492 }
493 sc.SubjectAltNameMatchers = matchers
494 return sc, nil
495 }
496
497
498
499
500 func circuitBreakersFromCluster(cluster *v3clusterpb.Cluster) *uint32 {
501 for _, threshold := range cluster.GetCircuitBreakers().GetThresholds() {
502 if threshold.GetPriority() != v3corepb.RoutingPriority_DEFAULT {
503 continue
504 }
505 maxRequestsPb := threshold.GetMaxRequests()
506 if maxRequestsPb == nil {
507 return nil
508 }
509 maxRequests := maxRequestsPb.GetValue()
510 return &maxRequests
511 }
512 return nil
513 }
514
515
516
517 func idurationp(d time.Duration) *iserviceconfig.Duration {
518 id := iserviceconfig.Duration(d)
519 return &id
520 }
521
522 func uint32p(i uint32) *uint32 {
523 return &i
524 }
525
526
527
528 type successRateEjection struct {
529 StdevFactor *uint32 `json:"stdevFactor,omitempty"`
530 EnforcementPercentage *uint32 `json:"enforcementPercentage,omitempty"`
531 MinimumHosts *uint32 `json:"minimumHosts,omitempty"`
532 RequestVolume *uint32 `json:"requestVolume,omitempty"`
533 }
534
535 type failurePercentageEjection struct {
536 Threshold *uint32 `json:"threshold,omitempty"`
537 EnforcementPercentage *uint32 `json:"enforcementPercentage,omitempty"`
538 MinimumHosts *uint32 `json:"minimumHosts,omitempty"`
539 RequestVolume *uint32 `json:"requestVolume,omitempty"`
540 }
541
542 type odLBConfig struct {
543 Interval *iserviceconfig.Duration `json:"interval,omitempty"`
544 BaseEjectionTime *iserviceconfig.Duration `json:"baseEjectionTime,omitempty"`
545 MaxEjectionTime *iserviceconfig.Duration `json:"maxEjectionTime,omitempty"`
546 MaxEjectionPercent *uint32 `json:"maxEjectionPercent,omitempty"`
547 SuccessRateEjection *successRateEjection `json:"successRateEjection,omitempty"`
548 FailurePercentageEjection *failurePercentageEjection `json:"failurePercentageEjection,omitempty"`
549 }
550
551
552
553
554
555 func outlierConfigFromCluster(cluster *v3clusterpb.Cluster) (json.RawMessage, error) {
556 od := cluster.GetOutlierDetection()
557 if od == nil {
558 return nil, nil
559 }
560
561
562
563
564
565
566
567
568
569 var interval *iserviceconfig.Duration
570 if i := od.GetInterval(); i != nil {
571 if err := i.CheckValid(); err != nil {
572 return nil, fmt.Errorf("outlier_detection.interval is invalid with error: %v", err)
573 }
574 if interval = idurationp(i.AsDuration()); *interval < 0 {
575 return nil, fmt.Errorf("outlier_detection.interval = %v; must be a valid duration and >= 0", *interval)
576 }
577 }
578
579 var baseEjectionTime *iserviceconfig.Duration
580 if bet := od.GetBaseEjectionTime(); bet != nil {
581 if err := bet.CheckValid(); err != nil {
582 return nil, fmt.Errorf("outlier_detection.base_ejection_time is invalid with error: %v", err)
583 }
584 if baseEjectionTime = idurationp(bet.AsDuration()); *baseEjectionTime < 0 {
585 return nil, fmt.Errorf("outlier_detection.base_ejection_time = %v; must be >= 0", *baseEjectionTime)
586 }
587 }
588
589 var maxEjectionTime *iserviceconfig.Duration
590 if met := od.GetMaxEjectionTime(); met != nil {
591 if err := met.CheckValid(); err != nil {
592 return nil, fmt.Errorf("outlier_detection.max_ejection_time is invalid: %v", err)
593 }
594 if maxEjectionTime = idurationp(met.AsDuration()); *maxEjectionTime < 0 {
595 return nil, fmt.Errorf("outlier_detection.max_ejection_time = %v; must be >= 0", *maxEjectionTime)
596 }
597 }
598
599
600
601
602
603 var maxEjectionPercent *uint32
604 if mep := od.GetMaxEjectionPercent(); mep != nil {
605 if maxEjectionPercent = uint32p(mep.GetValue()); *maxEjectionPercent > 100 {
606 return nil, fmt.Errorf("outlier_detection.max_ejection_percent = %v; must be <= 100", *maxEjectionPercent)
607 }
608 }
609
610
611
612 var enforcingSuccessRate *uint32
613 if esr := od.GetEnforcingSuccessRate(); esr != nil {
614 if enforcingSuccessRate = uint32p(esr.GetValue()); *enforcingSuccessRate > 100 {
615 return nil, fmt.Errorf("outlier_detection.enforcing_success_rate = %v; must be <= 100", *enforcingSuccessRate)
616 }
617 }
618 var failurePercentageThreshold *uint32
619 if fpt := od.GetFailurePercentageThreshold(); fpt != nil {
620 if failurePercentageThreshold = uint32p(fpt.GetValue()); *failurePercentageThreshold > 100 {
621 return nil, fmt.Errorf("outlier_detection.failure_percentage_threshold = %v; must be <= 100", *failurePercentageThreshold)
622 }
623 }
624
625
626
627 var enforcingFailurePercentage *uint32
628 if efp := od.GetEnforcingFailurePercentage(); efp != nil {
629 if enforcingFailurePercentage = uint32p(efp.GetValue()); *enforcingFailurePercentage > 100 {
630 return nil, fmt.Errorf("outlier_detection.enforcing_failure_percentage = %v; must be <= 100", *enforcingFailurePercentage)
631 }
632 }
633
634 var successRateStdevFactor *uint32
635 if srsf := od.GetSuccessRateStdevFactor(); srsf != nil {
636 successRateStdevFactor = uint32p(srsf.GetValue())
637 }
638 var successRateMinimumHosts *uint32
639 if srmh := od.GetSuccessRateMinimumHosts(); srmh != nil {
640 successRateMinimumHosts = uint32p(srmh.GetValue())
641 }
642 var successRateRequestVolume *uint32
643 if srrv := od.GetSuccessRateRequestVolume(); srrv != nil {
644 successRateRequestVolume = uint32p(srrv.GetValue())
645 }
646 var failurePercentageMinimumHosts *uint32
647 if fpmh := od.GetFailurePercentageMinimumHosts(); fpmh != nil {
648 failurePercentageMinimumHosts = uint32p(fpmh.GetValue())
649 }
650 var failurePercentageRequestVolume *uint32
651 if fprv := od.GetFailurePercentageRequestVolume(); fprv != nil {
652 failurePercentageRequestVolume = uint32p(fprv.GetValue())
653 }
654
655
656
657
658 var sre *successRateEjection
659 if enforcingSuccessRate == nil || *enforcingSuccessRate != 0 {
660 sre = &successRateEjection{
661 StdevFactor: successRateStdevFactor,
662 EnforcementPercentage: enforcingSuccessRate,
663 MinimumHosts: successRateMinimumHosts,
664 RequestVolume: successRateRequestVolume,
665 }
666 }
667
668
669
670
671 var fpe *failurePercentageEjection
672 if enforcingFailurePercentage != nil && *enforcingFailurePercentage != 0 {
673 fpe = &failurePercentageEjection{
674 Threshold: failurePercentageThreshold,
675 EnforcementPercentage: enforcingFailurePercentage,
676 MinimumHosts: failurePercentageMinimumHosts,
677 RequestVolume: failurePercentageRequestVolume,
678 }
679 }
680
681 odLBCfg := &odLBConfig{
682 Interval: interval,
683 BaseEjectionTime: baseEjectionTime,
684 MaxEjectionTime: maxEjectionTime,
685 MaxEjectionPercent: maxEjectionPercent,
686 SuccessRateEjection: sre,
687 FailurePercentageEjection: fpe,
688 }
689 return json.Marshal(odLBCfg)
690 }
691
View as plain text