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 }