1
18
19 package wrrlocality
20
21 import (
22 "context"
23 "encoding/json"
24 "errors"
25 "strings"
26 "testing"
27 "time"
28
29 "github.com/google/go-cmp/cmp"
30 "google.golang.org/grpc/balancer"
31 "google.golang.org/grpc/balancer/roundrobin"
32 "google.golang.org/grpc/balancer/weightedtarget"
33 "google.golang.org/grpc/internal/balancer/stub"
34 "google.golang.org/grpc/internal/grpctest"
35 internalserviceconfig "google.golang.org/grpc/internal/serviceconfig"
36 "google.golang.org/grpc/internal/testutils"
37 "google.golang.org/grpc/resolver"
38 "google.golang.org/grpc/serviceconfig"
39 "google.golang.org/grpc/xds/internal"
40 )
41
42 const (
43 defaultTestTimeout = 5 * time.Second
44 )
45
46 type s struct {
47 grpctest.Tester
48 }
49
50 func Test(t *testing.T) {
51 grpctest.RunSubTests(t, s{})
52 }
53
54 func (s) TestParseConfig(t *testing.T) {
55 const errParseConfigName = "errParseConfigBalancer"
56 stub.Register(errParseConfigName, stub.BalancerFuncs{
57 ParseConfig: func(json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
58 return nil, errors.New("some error")
59 },
60 })
61
62 parser := bb{}
63 tests := []struct {
64 name string
65 input string
66 wantCfg serviceconfig.LoadBalancingConfig
67 wantErr string
68 }{
69 {
70 name: "happy-case-round robin-child",
71 input: `{"childPolicy": [{"round_robin": {}}]}`,
72 wantCfg: &LBConfig{
73 ChildPolicy: &internalserviceconfig.BalancerConfig{
74 Name: roundrobin.Name,
75 },
76 },
77 },
78 {
79 name: "invalid-json",
80 input: "{{invalidjson{{",
81 wantErr: "invalid character",
82 },
83
84 {
85 name: "child-policy-field-isn't-set",
86 input: `{}`,
87 wantErr: "child policy field must be set",
88 },
89 {
90 name: "child-policy-type-is-empty",
91 input: `{"childPolicy": []}`,
92 wantErr: "invalid loadBalancingConfig: no supported policies found in []",
93 },
94 {
95 name: "child-policy-empty-config",
96 input: `{"childPolicy": [{"": {}}]}`,
97 wantErr: "invalid loadBalancingConfig: no supported policies found in []",
98 },
99 {
100 name: "child-policy-type-isn't-registered",
101 input: `{"childPolicy": [{"doesNotExistBalancer": {"cluster": "test_cluster"}}]}`,
102 wantErr: "invalid loadBalancingConfig: no supported policies found in [doesNotExistBalancer]",
103 },
104 {
105 name: "child-policy-config-is-invalid",
106 input: `{"childPolicy": [{"errParseConfigBalancer": {"cluster": "test_cluster"}}]}`,
107 wantErr: "error parsing loadBalancingConfig for policy \"errParseConfigBalancer\"",
108 },
109 }
110 for _, test := range tests {
111 t.Run(test.name, func(t *testing.T) {
112 gotCfg, gotErr := parser.ParseConfig(json.RawMessage(test.input))
113
114
115
116
117
118 if (gotErr != nil) != (test.wantErr != "") {
119 t.Fatalf("ParseConfig(%v) = %v, wantErr %v", test.input, gotErr, test.wantErr)
120 }
121 if gotErr != nil && !strings.Contains(gotErr.Error(), test.wantErr) {
122 t.Fatalf("ParseConfig(%v) = %v, wantErr %v", test.input, gotErr, test.wantErr)
123 }
124 if test.wantErr != "" {
125 return
126 }
127 if diff := cmp.Diff(gotCfg, test.wantCfg); diff != "" {
128 t.Fatalf("ParseConfig(%v) got unexpected output, diff (-got +want): %v", test.input, diff)
129 }
130 })
131 }
132 }
133
134
135
136
137
138
139 func (s) TestUpdateClientConnState(t *testing.T) {
140
141
142 cfgCh := testutils.NewChannel()
143 oldWeightedTargetName := weightedTargetName
144 defer func() {
145 weightedTargetName = oldWeightedTargetName
146 }()
147 weightedTargetName = "fake_weighted_target"
148 stub.Register("fake_weighted_target", stub.BalancerFuncs{
149 ParseConfig: func(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
150 var cfg weightedtarget.LBConfig
151 if err := json.Unmarshal(c, &cfg); err != nil {
152 return nil, err
153 }
154 return &cfg, nil
155 },
156 UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
157 wtCfg, ok := ccs.BalancerConfig.(*weightedtarget.LBConfig)
158 if !ok {
159 return errors.New("child received config that was not a weighted target config")
160 }
161 defer cfgCh.Send(wtCfg)
162 return nil
163 },
164 })
165
166 builder := balancer.Get(Name)
167 if builder == nil {
168 t.Fatalf("balancer.Get(%q) returned nil", Name)
169 }
170 tcc := testutils.NewBalancerClientConn(t)
171 bal := builder.Build(tcc, balancer.BuildOptions{})
172 defer bal.Close()
173 wrrL := bal.(*wrrLocalityBalancer)
174
175
176
177
178 addr1 := resolver.Address{
179 Addr: "locality-1",
180 }
181 addr1 = internal.SetLocalityID(addr1, internal.LocalityID{
182 Region: "region-1",
183 Zone: "zone-1",
184 SubZone: "subzone-1",
185 })
186 addr1 = SetAddrInfo(addr1, AddrInfo{LocalityWeight: 2})
187
188 addr2 := resolver.Address{
189 Addr: "locality-2",
190 }
191 addr2 = internal.SetLocalityID(addr2, internal.LocalityID{
192 Region: "region-2",
193 Zone: "zone-2",
194 SubZone: "subzone-2",
195 })
196 addr2 = SetAddrInfo(addr2, AddrInfo{LocalityWeight: 1})
197 addrs := []resolver.Address{addr1, addr2}
198
199 err := wrrL.UpdateClientConnState(balancer.ClientConnState{
200 BalancerConfig: &LBConfig{
201 ChildPolicy: &internalserviceconfig.BalancerConfig{
202 Name: "round_robin",
203 },
204 },
205 ResolverState: resolver.State{
206 Addresses: addrs,
207 },
208 })
209 if err != nil {
210 t.Fatalf("Unexpected error from UpdateClientConnState: %v", err)
211 }
212
213
214
215
216
217
218
219 wantWtCfg := &weightedtarget.LBConfig{
220 Targets: map[string]weightedtarget.Target{
221 "{\"region\":\"region-1\",\"zone\":\"zone-1\",\"subZone\":\"subzone-1\"}": {
222 Weight: 2,
223 ChildPolicy: &internalserviceconfig.BalancerConfig{
224 Name: "round_robin",
225 },
226 },
227 "{\"region\":\"region-2\",\"zone\":\"zone-2\",\"subZone\":\"subzone-2\"}": {
228 Weight: 1,
229 ChildPolicy: &internalserviceconfig.BalancerConfig{
230 Name: "round_robin",
231 },
232 },
233 },
234 }
235
236 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
237 defer cancel()
238 cfg, err := cfgCh.Receive(ctx)
239 if err != nil {
240 t.Fatalf("No signal received from UpdateClientConnState() on the child: %v", err)
241 }
242
243 gotWtCfg, ok := cfg.(*weightedtarget.LBConfig)
244 if !ok {
245
246 t.Fatalf("Unexpected config type: %T", gotWtCfg)
247 }
248
249 if diff := cmp.Diff(gotWtCfg, wantWtCfg); diff != "" {
250 t.Fatalf("Child received unexpected config, diff (-got, +want): %v", diff)
251 }
252 }
253
View as plain text