google.golang.org/grpc@v1.62.1/xds/internal/balancer/clusterresolver/config_test.go (about) 1 /* 2 * 3 * Copyright 2021 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 clusterresolver 20 21 import ( 22 "encoding/json" 23 "testing" 24 "time" 25 26 "github.com/google/go-cmp/cmp" 27 "github.com/google/go-cmp/cmp/cmpopts" 28 "google.golang.org/grpc/balancer" 29 "google.golang.org/grpc/balancer/roundrobin" 30 iserviceconfig "google.golang.org/grpc/internal/serviceconfig" 31 "google.golang.org/grpc/xds/internal/balancer/outlierdetection" 32 "google.golang.org/grpc/xds/internal/balancer/ringhash" 33 "google.golang.org/grpc/xds/internal/xdsclient/bootstrap" 34 ) 35 36 func TestDiscoveryMechanismTypeMarshalJSON(t *testing.T) { 37 tests := []struct { 38 name string 39 typ DiscoveryMechanismType 40 want string 41 }{ 42 { 43 name: "eds", 44 typ: DiscoveryMechanismTypeEDS, 45 want: `"EDS"`, 46 }, 47 { 48 name: "dns", 49 typ: DiscoveryMechanismTypeLogicalDNS, 50 want: `"LOGICAL_DNS"`, 51 }, 52 } 53 for _, tt := range tests { 54 t.Run(tt.name, func(t *testing.T) { 55 if got, err := json.Marshal(tt.typ); err != nil || string(got) != tt.want { 56 t.Fatalf("DiscoveryMechanismTypeEDS.MarshalJSON() = (%v, %v), want (%s, nil)", string(got), err, tt.want) 57 } 58 }) 59 } 60 } 61 func TestDiscoveryMechanismTypeUnmarshalJSON(t *testing.T) { 62 tests := []struct { 63 name string 64 js string 65 want DiscoveryMechanismType 66 wantErr bool 67 }{ 68 { 69 name: "eds", 70 js: `"EDS"`, 71 want: DiscoveryMechanismTypeEDS, 72 }, 73 { 74 name: "dns", 75 js: `"LOGICAL_DNS"`, 76 want: DiscoveryMechanismTypeLogicalDNS, 77 }, 78 { 79 name: "error", 80 js: `"1234"`, 81 wantErr: true, 82 }, 83 } 84 for _, tt := range tests { 85 t.Run(tt.name, func(t *testing.T) { 86 var got DiscoveryMechanismType 87 err := json.Unmarshal([]byte(tt.js), &got) 88 if (err != nil) != tt.wantErr { 89 t.Fatalf("DiscoveryMechanismTypeEDS.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) 90 } 91 if diff := cmp.Diff(got, tt.want); diff != "" { 92 t.Fatalf("DiscoveryMechanismTypeEDS.UnmarshalJSON() got unexpected output, diff (-got +want): %v", diff) 93 } 94 }) 95 } 96 } 97 98 const ( 99 testJSONConfig1 = `{ 100 "discoveryMechanisms": [{ 101 "cluster": "test-cluster-name", 102 "lrsLoadReportingServer": { 103 "server_uri": "trafficdirector.googleapis.com:443", 104 "channel_creds": [ { "type": "google_default" } ] 105 }, 106 "maxConcurrentRequests": 314, 107 "type": "EDS", 108 "edsServiceName": "test-eds-service-name", 109 "outlierDetection": {} 110 }], 111 "xdsLbPolicy":[{"round_robin":{}}] 112 }` 113 testJSONConfig2 = `{ 114 "discoveryMechanisms": [{ 115 "cluster": "test-cluster-name", 116 "lrsLoadReportingServer": { 117 "server_uri": "trafficdirector.googleapis.com:443", 118 "channel_creds": [ { "type": "google_default" } ] 119 }, 120 "maxConcurrentRequests": 314, 121 "type": "EDS", 122 "edsServiceName": "test-eds-service-name", 123 "outlierDetection": {} 124 },{ 125 "type": "LOGICAL_DNS", 126 "outlierDetection": {} 127 }], 128 "xdsLbPolicy":[{"round_robin":{}}] 129 }` 130 testJSONConfig3 = `{ 131 "discoveryMechanisms": [{ 132 "cluster": "test-cluster-name", 133 "lrsLoadReportingServer": { 134 "server_uri": "trafficdirector.googleapis.com:443", 135 "channel_creds": [ { "type": "google_default" } ] 136 }, 137 "maxConcurrentRequests": 314, 138 "type": "EDS", 139 "edsServiceName": "test-eds-service-name", 140 "outlierDetection": {} 141 }], 142 "xdsLbPolicy":[{"round_robin":{}}] 143 }` 144 testJSONConfig4 = `{ 145 "discoveryMechanisms": [{ 146 "cluster": "test-cluster-name", 147 "lrsLoadReportingServer": { 148 "server_uri": "trafficdirector.googleapis.com:443", 149 "channel_creds": [ { "type": "google_default" } ] 150 }, 151 "maxConcurrentRequests": 314, 152 "type": "EDS", 153 "edsServiceName": "test-eds-service-name", 154 "outlierDetection": {} 155 }], 156 "xdsLbPolicy":[{"ring_hash_experimental":{}}] 157 }` 158 testJSONConfig5 = `{ 159 "discoveryMechanisms": [{ 160 "cluster": "test-cluster-name", 161 "lrsLoadReportingServer": { 162 "server_uri": "trafficdirector.googleapis.com:443", 163 "channel_creds": [ { "type": "google_default" } ] 164 }, 165 "maxConcurrentRequests": 314, 166 "type": "EDS", 167 "edsServiceName": "test-eds-service-name", 168 "outlierDetection": {} 169 }], 170 "xdsLbPolicy":[{"round_robin":{}}] 171 }` 172 ) 173 174 var testLRSServerConfig = &bootstrap.ServerConfig{ 175 ServerURI: "trafficdirector.googleapis.com:443", 176 Creds: bootstrap.ChannelCreds{ 177 Type: "google_default", 178 }, 179 } 180 181 func TestParseConfig(t *testing.T) { 182 tests := []struct { 183 name string 184 js string 185 want *LBConfig 186 wantErr bool 187 }{ 188 { 189 name: "empty json", 190 js: "", 191 want: nil, 192 wantErr: true, 193 }, 194 { 195 name: "OK with one discovery mechanism", 196 js: testJSONConfig1, 197 want: &LBConfig{ 198 DiscoveryMechanisms: []DiscoveryMechanism{ 199 { 200 Cluster: testClusterName, 201 LoadReportingServer: testLRSServerConfig, 202 MaxConcurrentRequests: newUint32(testMaxRequests), 203 Type: DiscoveryMechanismTypeEDS, 204 EDSServiceName: testEDSService, 205 outlierDetection: outlierdetection.LBConfig{ 206 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 207 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 208 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 209 MaxEjectionPercent: 10, 210 // sre and fpe are both nil 211 }, 212 }, 213 }, 214 xdsLBPolicy: iserviceconfig.BalancerConfig{ // do we want to make this not pointer 215 Name: roundrobin.Name, 216 Config: nil, 217 }, 218 }, 219 wantErr: false, 220 }, 221 { 222 name: "OK with multiple discovery mechanisms", 223 js: testJSONConfig2, 224 want: &LBConfig{ 225 DiscoveryMechanisms: []DiscoveryMechanism{ 226 { 227 Cluster: testClusterName, 228 LoadReportingServer: testLRSServerConfig, 229 MaxConcurrentRequests: newUint32(testMaxRequests), 230 Type: DiscoveryMechanismTypeEDS, 231 EDSServiceName: testEDSService, 232 outlierDetection: outlierdetection.LBConfig{ 233 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 234 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 235 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 236 MaxEjectionPercent: 10, 237 // sre and fpe are both nil 238 }, 239 }, 240 { 241 Type: DiscoveryMechanismTypeLogicalDNS, 242 outlierDetection: outlierdetection.LBConfig{ 243 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 244 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 245 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 246 MaxEjectionPercent: 10, 247 // sre and fpe are both nil 248 }, 249 }, 250 }, 251 xdsLBPolicy: iserviceconfig.BalancerConfig{ 252 Name: roundrobin.Name, 253 Config: nil, 254 }, 255 }, 256 wantErr: false, 257 }, 258 { 259 name: "OK with picking policy round_robin", 260 js: testJSONConfig3, 261 want: &LBConfig{ 262 DiscoveryMechanisms: []DiscoveryMechanism{ 263 { 264 Cluster: testClusterName, 265 LoadReportingServer: testLRSServerConfig, 266 MaxConcurrentRequests: newUint32(testMaxRequests), 267 Type: DiscoveryMechanismTypeEDS, 268 EDSServiceName: testEDSService, 269 outlierDetection: outlierdetection.LBConfig{ 270 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 271 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 272 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 273 MaxEjectionPercent: 10, 274 // sre and fpe are both nil 275 }, 276 }, 277 }, 278 xdsLBPolicy: iserviceconfig.BalancerConfig{ 279 Name: roundrobin.Name, 280 Config: nil, 281 }, 282 }, 283 wantErr: false, 284 }, 285 { 286 name: "OK with picking policy ring_hash", 287 js: testJSONConfig4, 288 want: &LBConfig{ 289 DiscoveryMechanisms: []DiscoveryMechanism{ 290 { 291 Cluster: testClusterName, 292 LoadReportingServer: testLRSServerConfig, 293 MaxConcurrentRequests: newUint32(testMaxRequests), 294 Type: DiscoveryMechanismTypeEDS, 295 EDSServiceName: testEDSService, 296 outlierDetection: outlierdetection.LBConfig{ 297 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 298 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 299 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 300 MaxEjectionPercent: 10, 301 // sre and fpe are both nil 302 }, 303 }, 304 }, 305 xdsLBPolicy: iserviceconfig.BalancerConfig{ 306 Name: ringhash.Name, 307 Config: &ringhash.LBConfig{MinRingSize: 1024, MaxRingSize: 4096}, // Ringhash LB config with default min and max. 308 }, 309 }, 310 wantErr: false, 311 }, 312 { 313 name: "noop-outlier-detection", 314 js: testJSONConfig5, 315 want: &LBConfig{ 316 DiscoveryMechanisms: []DiscoveryMechanism{ 317 { 318 Cluster: testClusterName, 319 LoadReportingServer: testLRSServerConfig, 320 MaxConcurrentRequests: newUint32(testMaxRequests), 321 Type: DiscoveryMechanismTypeEDS, 322 EDSServiceName: testEDSService, 323 outlierDetection: outlierdetection.LBConfig{ 324 Interval: iserviceconfig.Duration(10 * time.Second), // default interval 325 BaseEjectionTime: iserviceconfig.Duration(30 * time.Second), 326 MaxEjectionTime: iserviceconfig.Duration(300 * time.Second), 327 MaxEjectionPercent: 10, 328 // sre and fpe are both nil 329 }, 330 }, 331 }, 332 xdsLBPolicy: iserviceconfig.BalancerConfig{ 333 Name: roundrobin.Name, 334 Config: nil, 335 }, 336 }, 337 wantErr: false, 338 }, 339 } 340 for _, tt := range tests { 341 b := balancer.Get(Name) 342 if b == nil { 343 t.Fatalf("LB policy %q not registered", Name) 344 } 345 cfgParser, ok := b.(balancer.ConfigParser) 346 if !ok { 347 t.Fatalf("LB policy %q does not support config parsing", Name) 348 } 349 t.Run(tt.name, func(t *testing.T) { 350 got, err := cfgParser.ParseConfig([]byte(tt.js)) 351 if (err != nil) != tt.wantErr { 352 t.Fatalf("parseConfig() error = %v, wantErr %v", err, tt.wantErr) 353 } 354 if tt.wantErr { 355 return 356 } 357 if diff := cmp.Diff(got, tt.want, cmp.AllowUnexported(LBConfig{}), cmpopts.IgnoreFields(LBConfig{}, "XDSLBPolicy")); diff != "" { 358 t.Errorf("parseConfig() got unexpected output, diff (-got +want): %v", diff) 359 } 360 }) 361 } 362 } 363 364 func newUint32(i uint32) *uint32 { 365 return &i 366 }