google.golang.org/grpc@v1.72.2/xds/internal/resolver/serviceconfig_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 resolver
    20  
    21  import (
    22  	"context"
    23  	"regexp"
    24  	"testing"
    25  	"time"
    26  
    27  	xxhash "github.com/cespare/xxhash/v2"
    28  	"github.com/google/go-cmp/cmp"
    29  	"google.golang.org/grpc/internal/grpctest"
    30  	"google.golang.org/grpc/internal/grpcutil"
    31  	iresolver "google.golang.org/grpc/internal/resolver"
    32  	"google.golang.org/grpc/internal/testutils"
    33  	"google.golang.org/grpc/metadata"
    34  	_ "google.golang.org/grpc/xds/internal/balancer/cdsbalancer" // To parse LB config
    35  	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
    36  )
    37  
    38  var defaultTestTimeout = 10 * time.Second
    39  
    40  type s struct {
    41  	grpctest.Tester
    42  }
    43  
    44  func Test(t *testing.T) {
    45  	grpctest.RunSubTests(t, s{})
    46  }
    47  
    48  func (s) TestPruneActiveClusters(t *testing.T) {
    49  	r := &xdsResolver{activeClusters: map[string]*clusterInfo{
    50  		"zero":        {refCount: 0},
    51  		"one":         {refCount: 1},
    52  		"two":         {refCount: 2},
    53  		"anotherzero": {refCount: 0},
    54  	}}
    55  	want := map[string]*clusterInfo{
    56  		"one": {refCount: 1},
    57  		"two": {refCount: 2},
    58  	}
    59  	r.pruneActiveClusters()
    60  	if d := cmp.Diff(r.activeClusters, want, cmp.AllowUnexported(clusterInfo{})); d != "" {
    61  		t.Fatalf("r.activeClusters = %v; want %v\nDiffs: %v", r.activeClusters, want, d)
    62  	}
    63  }
    64  
    65  func (s) TestGenerateRequestHash(t *testing.T) {
    66  	const channelID = 12378921
    67  	cs := &configSelector{
    68  		r: &xdsResolver{
    69  			cc:        &testutils.ResolverClientConn{Logger: t},
    70  			channelID: channelID,
    71  		},
    72  	}
    73  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
    74  	defer cancel()
    75  	tests := []struct {
    76  		name            string
    77  		hashPolicies    []*xdsresource.HashPolicy
    78  		requestHashWant uint64
    79  		rpcInfo         iresolver.RPCInfo
    80  	}{
    81  		// TestGenerateRequestHashHeaders tests generating request hashes for
    82  		// hash policies that specify to hash headers.
    83  		{
    84  			name: "test-generate-request-hash-headers",
    85  			hashPolicies: []*xdsresource.HashPolicy{{
    86  				HashPolicyType:    xdsresource.HashPolicyTypeHeader,
    87  				HeaderName:        ":path",
    88  				Regex:             func() *regexp.Regexp { return regexp.MustCompile("/products") }(), // Will replace /products with /new-products, to test find and replace functionality.
    89  				RegexSubstitution: "/new-products",
    90  			}},
    91  			requestHashWant: xxhash.Sum64String("/new-products"),
    92  			rpcInfo: iresolver.RPCInfo{
    93  				Context: metadata.NewOutgoingContext(ctx, metadata.Pairs(":path", "/products")),
    94  				Method:  "/some-method",
    95  			},
    96  		},
    97  		// TestGenerateHashChannelID tests generating request hashes for hash
    98  		// policies that specify to hash something that uniquely identifies the
    99  		// ClientConn (the pointer).
   100  		{
   101  			name: "test-generate-request-hash-channel-id",
   102  			hashPolicies: []*xdsresource.HashPolicy{{
   103  				HashPolicyType: xdsresource.HashPolicyTypeChannelID,
   104  			}},
   105  			requestHashWant: channelID,
   106  			rpcInfo:         iresolver.RPCInfo{},
   107  		},
   108  		// TestGenerateRequestHashEmptyString tests generating request hashes
   109  		// for hash policies that specify to hash headers and replace empty
   110  		// strings in the headers.
   111  		{
   112  			name: "test-generate-request-hash-empty-string",
   113  			hashPolicies: []*xdsresource.HashPolicy{{
   114  				HashPolicyType:    xdsresource.HashPolicyTypeHeader,
   115  				HeaderName:        ":path",
   116  				Regex:             func() *regexp.Regexp { return regexp.MustCompile("") }(),
   117  				RegexSubstitution: "e",
   118  			}},
   119  			requestHashWant: xxhash.Sum64String("eaebece"),
   120  			rpcInfo: iresolver.RPCInfo{
   121  				Context: metadata.NewOutgoingContext(ctx, metadata.Pairs(":path", "abc")),
   122  				Method:  "/some-method",
   123  			},
   124  		},
   125  		// Tests that bin headers are skipped.
   126  		{
   127  			name: "skip-bin",
   128  			hashPolicies: []*xdsresource.HashPolicy{{
   129  				HashPolicyType: xdsresource.HashPolicyTypeHeader,
   130  				HeaderName:     "something-bin",
   131  			}, {
   132  				HashPolicyType: xdsresource.HashPolicyTypeChannelID,
   133  			}},
   134  			requestHashWant: channelID,
   135  			rpcInfo: iresolver.RPCInfo{
   136  				Context: metadata.NewOutgoingContext(ctx, metadata.Pairs("something-bin", "xyz")),
   137  			},
   138  		},
   139  		// Tests that extra metadata takes precedence over the user's metadata.
   140  		{
   141  			name: "extra-metadata",
   142  			hashPolicies: []*xdsresource.HashPolicy{{
   143  				HashPolicyType: xdsresource.HashPolicyTypeHeader,
   144  				HeaderName:     "content-type",
   145  			}},
   146  			requestHashWant: xxhash.Sum64String("grpc value"),
   147  			rpcInfo: iresolver.RPCInfo{
   148  				Context: grpcutil.WithExtraMetadata(
   149  					metadata.NewOutgoingContext(ctx, metadata.Pairs("content-type", "user value")),
   150  					metadata.Pairs("content-type", "grpc value"),
   151  				),
   152  			},
   153  		},
   154  	}
   155  	for _, test := range tests {
   156  		t.Run(test.name, func(t *testing.T) {
   157  			requestHashGot := cs.generateHash(test.rpcInfo, test.hashPolicies)
   158  			if requestHashGot != test.requestHashWant {
   159  				t.Fatalf("requestHashGot = %v, requestHashWant = %v", requestHashGot, test.requestHashWant)
   160  			}
   161  		})
   162  	}
   163  }