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 }