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