/* * * Copyright 2020 gRPC authors. * * Licensed 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 resolver import ( "encoding/json" "fmt" wrapperspb "github.com/golang/protobuf/ptypes/wrappers" xdsclient "google.golang.org/grpc/xds/internal/client" ) const ( cdsName = "cds_experimental" weightedTargetName = "weighted_target_experimental" xdsRoutingName = "xds_routing_experimental" ) type serviceConfig struct { LoadBalancingConfig balancerConfig `json:"loadBalancingConfig"` } type balancerConfig []map[string]interface{} func newBalancerConfig(name string, config interface{}) balancerConfig { return []map[string]interface{}{{name: config}} } type weightedCDSBalancerConfig struct { Targets map[string]cdsWithWeight `json:"targets"` } type cdsWithWeight struct { Weight uint32 `json:"weight"` ChildPolicy balancerConfig `json:"childPolicy"` } type cdsBalancerConfig struct { Cluster string `json:"cluster"` } type route struct { Path *string `json:"path,omitempty"` Prefix *string `json:"prefix,omitempty"` Regex *string `json:"regex,omitempty"` Headers []*xdsclient.HeaderMatcher `json:"headers,omitempty"` Fraction *wrapperspb.UInt32Value `json:"matchFraction,omitempty"` Action string `json:"action"` } type xdsActionConfig struct { ChildPolicy balancerConfig `json:"childPolicy"` } type xdsRoutingBalancerConfig struct { Action map[string]xdsActionConfig `json:"action"` Route []*route `json:"route"` } func (r *xdsResolver) routesToJSON(routes []*xdsclient.Route) (string, error) { r.updateActions(newActionsFromRoutes(routes)) // Generate routes. var rts []*route for _, rt := range routes { t := &route{ Path: rt.Path, Prefix: rt.Prefix, Regex: rt.Regex, Headers: rt.Headers, } if f := rt.Fraction; f != nil { t.Fraction = &wrapperspb.UInt32Value{Value: *f} } t.Action = r.getActionAssignedName(rt.Action) rts = append(rts, t) } // Generate actions. action := make(map[string]xdsActionConfig) for _, act := range r.actions { action[act.assignedName] = xdsActionConfig{ ChildPolicy: weightedClusterToBalancerConfig(act.clustersWithWeights), } } sc := serviceConfig{ LoadBalancingConfig: newBalancerConfig( xdsRoutingName, xdsRoutingBalancerConfig{ Route: rts, Action: action, }, ), } bs, err := json.Marshal(sc) if err != nil { return "", fmt.Errorf("failed to marshal json: %v", err) } return string(bs), nil } func weightedClusterToBalancerConfig(wc map[string]uint32) balancerConfig { // Even if WeightedCluster has only one entry, we still use weighted_target // as top level balancer, to avoid switching top policy between CDS and // weighted_target, causing TCP connection to be recreated. targets := make(map[string]cdsWithWeight) for name, weight := range wc { targets[name] = cdsWithWeight{ Weight: weight, ChildPolicy: newBalancerConfig(cdsName, cdsBalancerConfig{Cluster: name}), } } bc := newBalancerConfig( weightedTargetName, weightedCDSBalancerConfig{ Targets: targets, }, ) return bc } func (r *xdsResolver) serviceUpdateToJSON(su xdsclient.ServiceUpdate) (string, error) { return r.routesToJSON(su.Routes) }