gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/balancer/rls/internal/config_test.go (about)

     1  /*
     2   *
     3   * Copyright 2020 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 rls
    20  
    21  import (
    22  	"encoding/json"
    23  	"fmt"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	_ "gitee.com/ks-custle/core-gm/grpc/balancer/grpclb"               // grpclb for config parsing.
    29  	_ "gitee.com/ks-custle/core-gm/grpc/internal/resolver/passthrough" // passthrough resolver.
    30  )
    31  
    32  // testEqual reports whether the lbCfgs a and b are equal. This is to be used
    33  // only from tests. This ignores the keyBuilderMap field because its internals
    34  // are not exported, and hence not possible to specify in the want section of
    35  // the test. This is fine because we already have tests to make sure that the
    36  // keyBuilder is parsed properly from the service config.
    37  func testEqual(a, b *lbConfig) bool {
    38  	return a.lookupService == b.lookupService &&
    39  		a.lookupServiceTimeout == b.lookupServiceTimeout &&
    40  		a.maxAge == b.maxAge &&
    41  		a.staleAge == b.staleAge &&
    42  		a.cacheSizeBytes == b.cacheSizeBytes &&
    43  		a.defaultTarget == b.defaultTarget &&
    44  		a.childPolicyName == b.childPolicyName &&
    45  		a.childPolicyTargetField == b.childPolicyTargetField &&
    46  		childPolicyConfigEqual(a.childPolicyConfig, b.childPolicyConfig)
    47  }
    48  
    49  // TestParseConfig verifies successful config parsing scenarios.
    50  func (s) TestParseConfig(t *testing.T) {
    51  	childPolicyTargetFieldVal, _ := json.Marshal(dummyChildPolicyTarget)
    52  	tests := []struct {
    53  		desc    string
    54  		input   []byte
    55  		wantCfg *lbConfig
    56  	}{
    57  		{
    58  			// This input validates a few cases:
    59  			// - A top-level unknown field should not fail.
    60  			// - An unknown field in routeLookupConfig proto should not fail.
    61  			// - lookupServiceTimeout is set to its default value, since it is not specified in the input.
    62  			// - maxAge is set to maxMaxAge since the value is too large in the input.
    63  			// - staleAge is ignore because it is higher than maxAge in the input.
    64  			// - cacheSizeBytes is greater than the hard upper limit of 5MB
    65  			desc: "with transformations 1",
    66  			input: []byte(`{
    67  				"top-level-unknown-field": "unknown-value",
    68  				"routeLookupConfig": {
    69  					"unknown-field": "unknown-value",
    70  					"grpcKeybuilders": [{
    71  						"names": [{"service": "service", "method": "method"}],
    72  						"headers": [{"key": "k1", "names": ["v1"]}]
    73  					}],
    74  					"lookupService": ":///target",
    75  					"maxAge" : "500s",
    76  					"staleAge": "600s",
    77  					"cacheSizeBytes": 100000000,
    78  					"defaultTarget": "passthrough:///default"
    79  				},
    80  				"childPolicy": [
    81  					{"cds_experimental": {"Cluster": "my-fav-cluster"}},
    82  					{"unknown-policy": {"unknown-field": "unknown-value"}},
    83  					{"grpclb": {"childPolicy": [{"pickfirst": {}}]}}
    84  				],
    85  				"childPolicyConfigTargetFieldName": "service_name"
    86  			}`),
    87  			wantCfg: &lbConfig{
    88  				lookupService:          ":///target",
    89  				lookupServiceTimeout:   10 * time.Second, // This is the default value.
    90  				maxAge:                 5 * time.Minute,  // This is max maxAge.
    91  				staleAge:               time.Duration(0), // StaleAge is ignore because it was higher than maxAge.
    92  				cacheSizeBytes:         maxCacheSize,
    93  				defaultTarget:          "passthrough:///default",
    94  				childPolicyName:        "grpclb",
    95  				childPolicyTargetField: "service_name",
    96  				childPolicyConfig: map[string]json.RawMessage{
    97  					"childPolicy":  json.RawMessage(`[{"pickfirst": {}}]`),
    98  					"service_name": json.RawMessage(childPolicyTargetFieldVal),
    99  				},
   100  			},
   101  		},
   102  		{
   103  			desc: "without transformations",
   104  			input: []byte(`{
   105  				"routeLookupConfig": {
   106  					"grpcKeybuilders": [{
   107  						"names": [{"service": "service", "method": "method"}],
   108  						"headers": [{"key": "k1", "names": ["v1"]}]
   109  					}],
   110  					"lookupService": "target",
   111  					"lookupServiceTimeout" : "100s",
   112  					"maxAge": "60s",
   113  					"staleAge" : "50s",
   114  					"cacheSizeBytes": 1000,
   115  					"defaultTarget": "passthrough:///default"
   116  				},
   117  				"childPolicy": [{"grpclb": {"childPolicy": [{"pickfirst": {}}]}}],
   118  				"childPolicyConfigTargetFieldName": "service_name"
   119  			}`),
   120  			wantCfg: &lbConfig{
   121  				lookupService:          "target",
   122  				lookupServiceTimeout:   100 * time.Second,
   123  				maxAge:                 60 * time.Second,
   124  				staleAge:               50 * time.Second,
   125  				cacheSizeBytes:         1000,
   126  				defaultTarget:          "passthrough:///default",
   127  				childPolicyName:        "grpclb",
   128  				childPolicyTargetField: "service_name",
   129  				childPolicyConfig: map[string]json.RawMessage{
   130  					"childPolicy":  json.RawMessage(`[{"pickfirst": {}}]`),
   131  					"service_name": json.RawMessage(childPolicyTargetFieldVal),
   132  				},
   133  			},
   134  		},
   135  	}
   136  
   137  	builder := rlsBB{}
   138  	for _, test := range tests {
   139  		t.Run(test.desc, func(t *testing.T) {
   140  			lbCfg, err := builder.ParseConfig(test.input)
   141  			if err != nil || !testEqual(lbCfg.(*lbConfig), test.wantCfg) {
   142  				t.Errorf("ParseConfig(%s) = {%+v, %v}, want {%+v, nil}", string(test.input), lbCfg, err, test.wantCfg)
   143  			}
   144  		})
   145  	}
   146  }
   147  
   148  // TestParseConfigErrors verifies config parsing failure scenarios.
   149  func (s) TestParseConfigErrors(t *testing.T) {
   150  	tests := []struct {
   151  		desc    string
   152  		input   []byte
   153  		wantErr string
   154  	}{
   155  		{
   156  			desc:    "empty input",
   157  			input:   nil,
   158  			wantErr: "rls: json unmarshal failed for service config",
   159  		},
   160  		{
   161  			desc:    "bad json",
   162  			input:   []byte(`bad bad json`),
   163  			wantErr: "rls: json unmarshal failed for service config",
   164  		},
   165  		{
   166  			desc: "bad grpcKeyBuilder",
   167  			input: []byte(`{
   168  					"routeLookupConfig": {
   169  						"grpcKeybuilders": [{
   170  							"names": [{"service": "service", "method": "method"}],
   171  							"headers": [{"key": "k1", "requiredMatch": true, "names": ["v1"]}]
   172  						}]
   173  					}
   174  				}`),
   175  			wantErr: "rls: GrpcKeyBuilder in RouteLookupConfig has required_match field set",
   176  		},
   177  		{
   178  			desc: "empty lookup service",
   179  			input: []byte(`{
   180  					"routeLookupConfig": {
   181  						"grpcKeybuilders": [{
   182  							"names": [{"service": "service", "method": "method"}],
   183  							"headers": [{"key": "k1", "names": ["v1"]}]
   184  						}]
   185  					}
   186  				}`),
   187  			wantErr: "rls: empty lookup_service in route lookup config",
   188  		},
   189  		{
   190  			desc: "unregistered scheme in lookup service URI",
   191  			input: []byte(`{
   192  					"routeLookupConfig": {
   193  						"grpcKeybuilders": [{
   194  							"names": [{"service": "service", "method": "method"}],
   195  							"headers": [{"key": "k1", "names": ["v1"]}]
   196  						}],
   197  						"lookupService": "badScheme:///target"
   198  					}
   199  				}`),
   200  			wantErr: "rls: unregistered scheme in lookup_service",
   201  		},
   202  		{
   203  			desc: "invalid lookup service timeout",
   204  			input: []byte(`{
   205  				"routeLookupConfig": {
   206  					"grpcKeybuilders": [{
   207  						"names": [{"service": "service", "method": "method"}],
   208  						"headers": [{"key": "k1", "names": ["v1"]}]
   209  					}],
   210  					"lookupService": "passthrough:///target",
   211  					"lookupServiceTimeout" : "315576000001s"
   212  				}
   213  			}`),
   214  			wantErr: "google.protobuf.Duration value out of range",
   215  		},
   216  		{
   217  			desc: "invalid max age",
   218  			input: []byte(`{
   219  				"routeLookupConfig": {
   220  					"grpcKeybuilders": [{
   221  						"names": [{"service": "service", "method": "method"}],
   222  						"headers": [{"key": "k1", "names": ["v1"]}]
   223  					}],
   224  					"lookupService": "passthrough:///target",
   225  					"lookupServiceTimeout" : "10s",
   226  					"maxAge" : "315576000001s"
   227  				}
   228  			}`),
   229  			wantErr: "google.protobuf.Duration value out of range",
   230  		},
   231  		{
   232  			desc: "invalid stale age",
   233  			input: []byte(`{
   234  				"routeLookupConfig": {
   235  					"grpcKeybuilders": [{
   236  						"names": [{"service": "service", "method": "method"}],
   237  						"headers": [{"key": "k1", "names": ["v1"]}]
   238  					}],
   239  					"lookupService": "passthrough:///target",
   240  					"lookupServiceTimeout" : "10s",
   241  					"maxAge" : "10s",
   242  					"staleAge" : "315576000001s"
   243  				}
   244  			}`),
   245  			wantErr: "google.protobuf.Duration value out of range",
   246  		},
   247  		{
   248  			desc: "invalid max age stale age combo",
   249  			input: []byte(`{
   250  				"routeLookupConfig": {
   251  					"grpcKeybuilders": [{
   252  						"names": [{"service": "service", "method": "method"}],
   253  						"headers": [{"key": "k1", "names": ["v1"]}]
   254  					}],
   255  					"lookupService": "passthrough:///target",
   256  					"lookupServiceTimeout" : "10s",
   257  					"staleAge" : "10s"
   258  				}
   259  			}`),
   260  			wantErr: "rls: stale_age is set, but max_age is not in route lookup config",
   261  		},
   262  		{
   263  			desc: "cache_size_bytes field is not set",
   264  			input: []byte(`{
   265  				"routeLookupConfig": {
   266  					"grpcKeybuilders": [{
   267  						"names": [{"service": "service", "method": "method"}],
   268  						"headers": [{"key": "k1", "names": ["v1"]}]
   269  					}],
   270  					"lookupService": "passthrough:///target",
   271  					"lookupServiceTimeout" : "10s",
   272  					"maxAge": "30s",
   273  					"staleAge" : "25s",
   274  					"defaultTarget": "passthrough:///default"
   275  				},
   276  				"childPolicyConfigTargetFieldName": "service_name"
   277  			}`),
   278  			wantErr: "rls: cache_size_bytes must be set to a non-zero value",
   279  		},
   280  		{
   281  			desc: "no child policy",
   282  			input: []byte(`{
   283  				"routeLookupConfig": {
   284  					"grpcKeybuilders": [{
   285  						"names": [{"service": "service", "method": "method"}],
   286  						"headers": [{"key": "k1", "names": ["v1"]}]
   287  					}],
   288  					"lookupService": "passthrough:///target",
   289  					"lookupServiceTimeout" : "10s",
   290  					"maxAge": "30s",
   291  					"staleAge" : "25s",
   292  					"cacheSizeBytes": 1000,
   293  					"defaultTarget": "passthrough:///default"
   294  				},
   295  				"childPolicyConfigTargetFieldName": "service_name"
   296  			}`),
   297  			wantErr: "rls: invalid childPolicy config: no supported policies found",
   298  		},
   299  		{
   300  			desc: "no known child policy",
   301  			input: []byte(`{
   302  				"routeLookupConfig": {
   303  					"grpcKeybuilders": [{
   304  						"names": [{"service": "service", "method": "method"}],
   305  						"headers": [{"key": "k1", "names": ["v1"]}]
   306  					}],
   307  					"lookupService": "passthrough:///target",
   308  					"lookupServiceTimeout" : "10s",
   309  					"maxAge": "30s",
   310  					"staleAge" : "25s",
   311  					"cacheSizeBytes": 1000,
   312  					"defaultTarget": "passthrough:///default"
   313  				},
   314  				"childPolicy": [
   315  					{"cds_experimental": {"Cluster": "my-fav-cluster"}},
   316  					{"unknown-policy": {"unknown-field": "unknown-value"}}
   317  				],
   318  				"childPolicyConfigTargetFieldName": "service_name"
   319  			}`),
   320  			wantErr: "rls: invalid childPolicy config: no supported policies found",
   321  		},
   322  		{
   323  			desc: "invalid child policy config - more than one entry in map",
   324  			input: []byte(`{
   325  				"routeLookupConfig": {
   326  					"grpcKeybuilders": [{
   327  						"names": [{"service": "service", "method": "method"}],
   328  						"headers": [{"key": "k1", "names": ["v1"]}]
   329  					}],
   330  					"lookupService": "passthrough:///target",
   331  					"lookupServiceTimeout" : "10s",
   332  					"maxAge": "30s",
   333  					"staleAge" : "25s",
   334  					"cacheSizeBytes": 1000,
   335  					"defaultTarget": "passthrough:///default"
   336  				},
   337  				"childPolicy": [
   338  					{
   339  						"cds_experimental": {"Cluster": "my-fav-cluster"},
   340  						"unknown-policy": {"unknown-field": "unknown-value"}
   341  					}
   342  				],
   343  				"childPolicyConfigTargetFieldName": "service_name"
   344  			}`),
   345  			wantErr: "does not contain exactly 1 policy/config pair",
   346  		},
   347  		{
   348  			desc: "no childPolicyConfigTargetFieldName",
   349  			input: []byte(`{
   350  				"routeLookupConfig": {
   351  					"grpcKeybuilders": [{
   352  						"names": [{"service": "service", "method": "method"}],
   353  						"headers": [{"key": "k1", "names": ["v1"]}]
   354  					}],
   355  					"lookupService": "passthrough:///target",
   356  					"lookupServiceTimeout" : "10s",
   357  					"maxAge": "30s",
   358  					"staleAge" : "25s",
   359  					"cacheSizeBytes": 1000,
   360  					"defaultTarget": "passthrough:///default"
   361  				},
   362  				"childPolicy": [
   363  					{"cds_experimental": {"Cluster": "my-fav-cluster"}},
   364  					{"unknown-policy": {"unknown-field": "unknown-value"}},
   365  					{"grpclb": {}}
   366  				]
   367  			}`),
   368  			wantErr: "rls: childPolicyConfigTargetFieldName field is not set in service config",
   369  		},
   370  		{
   371  			desc: "child policy config validation failure",
   372  			input: []byte(`{
   373  				"routeLookupConfig": {
   374  					"grpcKeybuilders": [{
   375  						"names": [{"service": "service", "method": "method"}],
   376  						"headers": [{"key": "k1", "names": ["v1"]}]
   377  					}],
   378  					"lookupService": "passthrough:///target",
   379  					"lookupServiceTimeout" : "10s",
   380  					"maxAge": "30s",
   381  					"staleAge" : "25s",
   382  					"cacheSizeBytes": 1000,
   383  					"defaultTarget": "passthrough:///default"
   384  				},
   385  				"childPolicy": [
   386  					{"cds_experimental": {"Cluster": "my-fav-cluster"}},
   387  					{"unknown-policy": {"unknown-field": "unknown-value"}},
   388  					{"grpclb": {"childPolicy": "not-an-array"}}
   389  				],
   390  				"childPolicyConfigTargetFieldName": "service_name"
   391  			}`),
   392  			wantErr: "rls: childPolicy config validation failed",
   393  		},
   394  	}
   395  
   396  	builder := rlsBB{}
   397  	for _, test := range tests {
   398  		t.Run(test.desc, func(t *testing.T) {
   399  			lbCfg, err := builder.ParseConfig(test.input)
   400  			if lbCfg != nil || !strings.Contains(fmt.Sprint(err), test.wantErr) {
   401  				t.Errorf("ParseConfig(%s) = {%+v, %v}, want {nil, %s}", string(test.input), lbCfg, err, test.wantErr)
   402  			}
   403  		})
   404  	}
   405  }