...

Source file src/google.golang.org/grpc/service_config_test.go

Documentation: google.golang.org/grpc

     1  /*
     2   *
     3   * Copyright 2017 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package grpc
    20  
    21  import (
    22  	"encoding/json"
    23  	"fmt"
    24  	"reflect"
    25  	"testing"
    26  	"time"
    27  
    28  	"google.golang.org/grpc/balancer"
    29  	"google.golang.org/grpc/internal/balancer/gracefulswitch"
    30  	"google.golang.org/grpc/serviceconfig"
    31  )
    32  
    33  type parseTestCase struct {
    34  	scjs    string
    35  	wantSC  *ServiceConfig
    36  	wantErr bool
    37  }
    38  
    39  func lbConfigFor(t *testing.T, name string, cfg serviceconfig.LoadBalancingConfig) serviceconfig.LoadBalancingConfig {
    40  	if name == "" {
    41  		name = "pick_first"
    42  		cfg = struct {
    43  			serviceconfig.LoadBalancingConfig
    44  		}{}
    45  	}
    46  	d := []map[string]any{{name: cfg}}
    47  	strCfg, err := json.Marshal(d)
    48  	t.Logf("strCfg = %v", string(strCfg))
    49  	if err != nil {
    50  		t.Fatalf("Error parsing config: %v", err)
    51  	}
    52  	parsedCfg, err := gracefulswitch.ParseConfig(strCfg)
    53  	if err != nil {
    54  		t.Fatalf("Error parsing config: %v", err)
    55  	}
    56  	return parsedCfg
    57  }
    58  
    59  func runParseTests(t *testing.T, testCases []parseTestCase) {
    60  	t.Helper()
    61  	for i, c := range testCases {
    62  		t.Run(fmt.Sprint(i), func(t *testing.T) {
    63  			scpr := parseServiceConfig(c.scjs)
    64  			var sc *ServiceConfig
    65  			sc, _ = scpr.Config.(*ServiceConfig)
    66  			if !c.wantErr {
    67  				c.wantSC.rawJSONString = c.scjs
    68  			}
    69  			if c.wantErr != (scpr.Err != nil) || !reflect.DeepEqual(sc, c.wantSC) {
    70  				t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, scpr.Err, c.wantSC, c.wantErr)
    71  			}
    72  		})
    73  	}
    74  }
    75  
    76  type pbbData struct {
    77  	serviceconfig.LoadBalancingConfig
    78  	Foo string
    79  	Bar int
    80  }
    81  
    82  type parseBalancerBuilder struct{}
    83  
    84  func (parseBalancerBuilder) Name() string {
    85  	return "pbb"
    86  }
    87  
    88  func (parseBalancerBuilder) ParseConfig(c json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
    89  	d := pbbData{}
    90  	if err := json.Unmarshal(c, &d); err != nil {
    91  		return nil, err
    92  	}
    93  	return d, nil
    94  }
    95  
    96  func (parseBalancerBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
    97  	panic("unimplemented")
    98  }
    99  
   100  func init() {
   101  	balancer.Register(parseBalancerBuilder{})
   102  }
   103  
   104  func (s) TestParseLBConfig(t *testing.T) {
   105  	testcases := []parseTestCase{
   106  		{
   107  			`{
   108      "loadBalancingConfig": [{"pbb": { "foo": "hi" } }]
   109  }`,
   110  			&ServiceConfig{
   111  				Methods:  make(map[string]MethodConfig),
   112  				lbConfig: lbConfigFor(t, "pbb", pbbData{Foo: "hi"}),
   113  			},
   114  			false,
   115  		},
   116  	}
   117  	runParseTests(t, testcases)
   118  }
   119  
   120  func (s) TestParseNoLBConfigSupported(t *testing.T) {
   121  	// We have a loadBalancingConfig field but will not encounter a supported
   122  	// policy.  The config will be considered invalid in this case.
   123  	testcases := []parseTestCase{
   124  		{
   125  			scjs: `{
   126      "loadBalancingConfig": [{"not_a_balancer1": {} }, {"not_a_balancer2": {}}]
   127  }`,
   128  			wantErr: true,
   129  		}, {
   130  			scjs:    `{"loadBalancingConfig": []}`,
   131  			wantErr: true,
   132  		},
   133  	}
   134  	runParseTests(t, testcases)
   135  }
   136  
   137  func (s) TestParseLoadBalancer(t *testing.T) {
   138  	testcases := []parseTestCase{
   139  		{
   140  			`{
   141      "loadBalancingPolicy": "round_robin",
   142      "methodConfig": [
   143          {
   144              "name": [
   145                  {
   146                      "service": "foo",
   147                      "method": "Bar"
   148                  }
   149              ],
   150              "waitForReady": true
   151          }
   152      ]
   153  }`,
   154  			&ServiceConfig{
   155  				Methods: map[string]MethodConfig{
   156  					"/foo/Bar": {
   157  						WaitForReady: newBool(true),
   158  					},
   159  				},
   160  				lbConfig: lbConfigFor(t, "round_robin", nil),
   161  			},
   162  			false,
   163  		},
   164  		{
   165  			`{
   166      "loadBalancingPolicy": 1,
   167      "methodConfig": [
   168          {
   169              "name": [
   170                  {
   171                      "service": "foo",
   172                      "method": "Bar"
   173                  }
   174              ],
   175              "waitForReady": false
   176          }
   177      ]
   178  }`,
   179  			nil,
   180  			true,
   181  		},
   182  	}
   183  	runParseTests(t, testcases)
   184  }
   185  
   186  func (s) TestParseWaitForReady(t *testing.T) {
   187  	testcases := []parseTestCase{
   188  		{
   189  			`{
   190      "methodConfig": [
   191          {
   192              "name": [
   193                  {
   194                      "service": "foo",
   195                      "method": "Bar"
   196                  }
   197              ],
   198              "waitForReady": true
   199          }
   200      ]
   201  }`,
   202  			&ServiceConfig{
   203  				Methods: map[string]MethodConfig{
   204  					"/foo/Bar": {
   205  						WaitForReady: newBool(true),
   206  					},
   207  				},
   208  				lbConfig: lbConfigFor(t, "", nil),
   209  			},
   210  			false,
   211  		},
   212  		{
   213  			`{
   214      "methodConfig": [
   215          {
   216              "name": [
   217                  {
   218                      "service": "foo",
   219                      "method": "Bar"
   220                  }
   221              ],
   222              "waitForReady": false
   223          }
   224      ]
   225  }`,
   226  			&ServiceConfig{
   227  				Methods: map[string]MethodConfig{
   228  					"/foo/Bar": {
   229  						WaitForReady: newBool(false),
   230  					},
   231  				},
   232  				lbConfig: lbConfigFor(t, "", nil),
   233  			},
   234  			false,
   235  		},
   236  		{
   237  			`{
   238      "methodConfig": [
   239          {
   240              "name": [
   241                  {
   242                      "service": "foo",
   243                      "method": "Bar"
   244                  }
   245              ],
   246              "waitForReady": fall
   247          },
   248          {
   249              "name": [
   250                  {
   251                      "service": "foo",
   252                      "method": "Bar"
   253                  }
   254              ],
   255              "waitForReady": true
   256          }
   257      ]
   258  }`,
   259  			nil,
   260  			true,
   261  		},
   262  	}
   263  
   264  	runParseTests(t, testcases)
   265  }
   266  
   267  func (s) TestParseTimeOut(t *testing.T) {
   268  	testcases := []parseTestCase{
   269  		{
   270  			`{
   271      "methodConfig": [
   272          {
   273              "name": [
   274                  {
   275                      "service": "foo",
   276                      "method": "Bar"
   277                  }
   278              ],
   279              "timeout": "1s"
   280          }
   281      ]
   282  }`,
   283  			&ServiceConfig{
   284  				Methods: map[string]MethodConfig{
   285  					"/foo/Bar": {
   286  						Timeout: newDuration(time.Second),
   287  					},
   288  				},
   289  				lbConfig: lbConfigFor(t, "", nil),
   290  			},
   291  			false,
   292  		},
   293  		{
   294  			`{
   295      "methodConfig": [
   296          {
   297              "name": [
   298                  {
   299                      "service": "foo",
   300                      "method": "Bar"
   301                  }
   302              ],
   303              "timeout": "3c"
   304          }
   305      ]
   306  }`,
   307  			nil,
   308  			true,
   309  		},
   310  		{
   311  			`{
   312      "methodConfig": [
   313          {
   314              "name": [
   315                  {
   316                      "service": "foo",
   317                      "method": "Bar"
   318                  }
   319              ],
   320              "timeout": "3c"
   321          },
   322          {
   323              "name": [
   324                  {
   325                      "service": "foo",
   326                      "method": "Bar"
   327                  }
   328              ],
   329              "timeout": "1s"
   330          }
   331      ]
   332  }`,
   333  			nil,
   334  			true,
   335  		},
   336  	}
   337  
   338  	runParseTests(t, testcases)
   339  }
   340  
   341  func (s) TestParseMsgSize(t *testing.T) {
   342  	testcases := []parseTestCase{
   343  		{
   344  			`{
   345      "methodConfig": [
   346          {
   347              "name": [
   348                  {
   349                      "service": "foo",
   350                      "method": "Bar"
   351                  }
   352              ],
   353              "maxRequestMessageBytes": 1024,
   354              "maxResponseMessageBytes": 2048
   355          }
   356      ]
   357  }`,
   358  			&ServiceConfig{
   359  				Methods: map[string]MethodConfig{
   360  					"/foo/Bar": {
   361  						MaxReqSize:  newInt(1024),
   362  						MaxRespSize: newInt(2048),
   363  					},
   364  				},
   365  				lbConfig: lbConfigFor(t, "", nil),
   366  			},
   367  			false,
   368  		},
   369  		{
   370  			`{
   371      "methodConfig": [
   372          {
   373              "name": [
   374                  {
   375                      "service": "foo",
   376                      "method": "Bar"
   377                  }
   378              ],
   379              "maxRequestMessageBytes": "1024",
   380              "maxResponseMessageBytes": "2048"
   381          },
   382          {
   383              "name": [
   384                  {
   385                      "service": "foo",
   386                      "method": "Bar"
   387                  }
   388              ],
   389              "maxRequestMessageBytes": 1024,
   390              "maxResponseMessageBytes": 2048
   391          }
   392      ]
   393  }`,
   394  			nil,
   395  			true,
   396  		},
   397  	}
   398  
   399  	runParseTests(t, testcases)
   400  }
   401  func (s) TestParseDefaultMethodConfig(t *testing.T) {
   402  	dc := &ServiceConfig{
   403  		Methods: map[string]MethodConfig{
   404  			"": {WaitForReady: newBool(true)},
   405  		},
   406  		lbConfig: lbConfigFor(t, "", nil),
   407  	}
   408  
   409  	runParseTests(t, []parseTestCase{
   410  		{
   411  			`{
   412    "methodConfig": [{
   413      "name": [{}],
   414      "waitForReady": true
   415    }]
   416  }`,
   417  			dc,
   418  			false,
   419  		},
   420  		{
   421  			`{
   422    "methodConfig": [{
   423      "name": [{"service": null}],
   424      "waitForReady": true
   425    }]
   426  }`,
   427  			dc,
   428  			false,
   429  		},
   430  		{
   431  			`{
   432    "methodConfig": [{
   433      "name": [{"service": ""}],
   434      "waitForReady": true
   435    }]
   436  }`,
   437  			dc,
   438  			false,
   439  		},
   440  		{
   441  			`{
   442    "methodConfig": [{
   443      "name": [{"method": "Bar"}],
   444      "waitForReady": true
   445    }]
   446  }`,
   447  			nil,
   448  			true,
   449  		},
   450  		{
   451  			`{
   452    "methodConfig": [{
   453      "name": [{"service": "", "method": "Bar"}],
   454      "waitForReady": true
   455    }]
   456  }`,
   457  			nil,
   458  			true,
   459  		},
   460  	})
   461  }
   462  
   463  func (s) TestParseMethodConfigDuplicatedName(t *testing.T) {
   464  	runParseTests(t, []parseTestCase{
   465  		{
   466  			`{
   467    "methodConfig": [{
   468      "name": [
   469        {"service": "foo"},
   470        {"service": "foo"}
   471      ],
   472      "waitForReady": true
   473    }]
   474  }`, nil, true,
   475  		},
   476  	})
   477  }
   478  
   479  func newBool(b bool) *bool {
   480  	return &b
   481  }
   482  
   483  func newDuration(b time.Duration) *time.Duration {
   484  	return &b
   485  }
   486  

View as plain text