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  }