From a0d5a0a663f005dd62fdf35e5ad5b3a04f18c09e Mon Sep 17 00:00:00 2001 From: Menghan Li Date: Mon, 25 Mar 2019 14:50:38 -0700 Subject: [PATCH] actually parse service config --- balancer/grpclb/grpclb.go | 4 +- balancer/grpclb/grpclb_config.go | 87 +++++++++++++++ balancer/grpclb/grpclb_config_test.go | 154 ++++++++++++++++++++++++++ balancer/grpclb/grpclb_test.go | 2 +- 4 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 balancer/grpclb/grpclb_config.go create mode 100644 balancer/grpclb/grpclb_config_test.go diff --git a/balancer/grpclb/grpclb.go b/balancer/grpclb/grpclb.go index 7d6102141f34..12f7257871f7 100644 --- a/balancer/grpclb/grpclb.go +++ b/balancer/grpclb/grpclb.go @@ -374,8 +374,7 @@ func (lb *lbBalancer) handleServiceConfig(sc string) { lb.mu.Lock() defer lb.mu.Unlock() - // TODO: FIXME: actually parse the service config. - newUsePickFirst := strings.Contains(sc, `{"grpclb":{"childPolicy":[{"pickfirst":{}}]}}`) + newUsePickFirst := childIsPickFirst(sc) if lb.usePickFirst == newUsePickFirst { return } @@ -384,7 +383,6 @@ func (lb *lbBalancer) handleServiceConfig(sc string) { } lb.refreshSubConns(lb.backendAddrs, lb.inFallback, newUsePickFirst) lb.regeneratePicker(true) - } func (lb *lbBalancer) UpdateResolverState(rs resolver.State) { diff --git a/balancer/grpclb/grpclb_config.go b/balancer/grpclb/grpclb_config.go new file mode 100644 index 000000000000..a9891e502770 --- /dev/null +++ b/balancer/grpclb/grpclb_config.go @@ -0,0 +1,87 @@ +/* + * + * Copyright 2016 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 grpclb + +import ( + "encoding/json" + + "google.golang.org/grpc" + "google.golang.org/grpc/balancer/roundrobin" +) + +type serviceConfig struct { + LoadBalancingConfig *[]map[string]*grpclbServiceConfig +} + +type grpclbServiceConfig struct { + ChildPolicy *[]map[string]json.RawMessage +} + +func parseFullServiceConfig(s string) *serviceConfig { + var ret serviceConfig + err := json.Unmarshal([]byte(s), &ret) + if err != nil { + return nil + } + return &ret +} + +func parseServiceConfig(s string) *grpclbServiceConfig { + parsedSC := parseFullServiceConfig(s) + if parsedSC == nil { + return nil + } + lbConfigs := parsedSC.LoadBalancingConfig + if lbConfigs == nil { + return nil + } + for _, lbC := range *lbConfigs { + if v, ok := lbC[grpclbName]; ok { + return v + } + } + return nil +} + +const ( + roundRobinName = roundrobin.Name + pickFirstName = grpc.PickFirstBalancerName +) + +func childIsPickFirst(s string) bool { + parsedSC := parseServiceConfig(s) + if parsedSC == nil { + return false + } + childConfigs := parsedSC.ChildPolicy + if childConfigs == nil { + return false + } + for _, childC := range *childConfigs { + // If round_robin exists before pick_first, return false + if _, ok := childC[roundRobinName]; ok { + return false + } + // If pick_first is before round_robin, return true + if _, ok := childC[pickFirstName]; ok { + return true + } + } + return false +} diff --git a/balancer/grpclb/grpclb_config_test.go b/balancer/grpclb/grpclb_config_test.go new file mode 100644 index 000000000000..4cf78b8cb2cd --- /dev/null +++ b/balancer/grpclb/grpclb_config_test.go @@ -0,0 +1,154 @@ +/* + * + * Copyright 2016 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 grpclb + +import ( + "encoding/json" + "reflect" + "testing" +) + +func Test_parseFullServiceConfig(t *testing.T) { + tests := []struct { + name string + s string + want *serviceConfig + }{ + { + name: "empty", + s: "", + want: nil, + }, + { + name: "success1", + s: `{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pick_first":{}}]}}]}`, + want: &serviceConfig{ + LoadBalancingConfig: &[]map[string]*grpclbServiceConfig{ + {"grpclb": &grpclbServiceConfig{ + ChildPolicy: &[]map[string]json.RawMessage{ + {"pick_first": json.RawMessage("{}")}, + }, + }}, + }, + }, + }, + { + name: "success2", + s: `{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"round_robin":{}},{"pick_first":{}}]}}]}`, + want: &serviceConfig{ + LoadBalancingConfig: &[]map[string]*grpclbServiceConfig{ + {"grpclb": &grpclbServiceConfig{ + ChildPolicy: &[]map[string]json.RawMessage{ + {"round_robin": json.RawMessage("{}")}, + {"pick_first": json.RawMessage("{}")}, + }, + }}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := parseFullServiceConfig(tt.s); !reflect.DeepEqual(got, tt.want) { + t.Errorf("parseFullServiceConfig() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func Test_parseServiceConfig(t *testing.T) { + tests := []struct { + name string + s string + want *grpclbServiceConfig + }{ + { + name: "empty", + s: "", + want: nil, + }, + { + name: "success1", + s: `{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pick_first":{}}]}}]}`, + want: &grpclbServiceConfig{ + ChildPolicy: &[]map[string]json.RawMessage{ + {"pick_first": json.RawMessage("{}")}, + }, + }, + }, + { + name: "success2", + s: `{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"round_robin":{}},{"pick_first":{}}]}}]}`, + want: &grpclbServiceConfig{ + ChildPolicy: &[]map[string]json.RawMessage{ + {"round_robin": json.RawMessage("{}")}, + {"pick_first": json.RawMessage("{}")}, + }, + }, + }, + { + name: "no_grpclb", + s: `{"loadBalancingConfig":[{"notgrpclb":{"childPolicy":[{"round_robin":{}},{"pick_first":{}}]}}]}`, + want: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := parseServiceConfig(tt.s); !reflect.DeepEqual(got, tt.want) { + t.Errorf("parseFullServiceConfig() = %+v, want %+v", got, tt.want) + } + }) + } +} + +func Test_childIsPickFirst(t *testing.T) { + tests := []struct { + name string + s string + want bool + }{ + { + name: "invalid", + s: "", + want: false, + }, + { + name: "pickfirst_only", + s: `{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pick_first":{}}]}}]}`, + want: true, + }, + { + name: "pickfirst_before_rr", + s: `{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pick_first":{}},{"round_robin":{}}]}}]}`, + want: true, + }, + { + name: "rr_before_pickfirst", + s: `{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"round_robin":{}},{"pick_first":{}}]}}]}`, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := childIsPickFirst(tt.s); got != tt.want { + t.Errorf("childIsPickFirst() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/balancer/grpclb/grpclb_test.go b/balancer/grpclb/grpclb_test.go index 9a6eb362365b..d41d38ed6afe 100644 --- a/balancer/grpclb/grpclb_test.go +++ b/balancer/grpclb/grpclb_test.go @@ -752,7 +752,7 @@ func TestFallback(t *testing.T) { } func TestGRPCLBPickFirst(t *testing.T) { - const grpclbServiceConfigWithPickFirst = `{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pickfirst":{}}]}}]}` + const grpclbServiceConfigWithPickFirst = `{"loadBalancingConfig":[{"grpclb":{"childPolicy":[{"pick_first":{}}]}}]}` defer leakcheck.Check(t)