github.com/lfch/etcd-io/tests/v3@v3.0.0-20221004140520-eac99acd3e9d/integration/grpc_test.go (about) 1 // Copyright 2021 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package integration 16 17 import ( 18 "context" 19 tls "crypto/tls" 20 "fmt" 21 "strings" 22 "testing" 23 "time" 24 25 clientv3 "github.com/lfch/etcd-io/client/v3" 26 "github.com/lfch/etcd-io/tests/v3/framework/integration" 27 "google.golang.org/grpc" 28 ) 29 30 func TestAuthority(t *testing.T) { 31 tcs := []struct { 32 name string 33 useTCP bool 34 useTLS bool 35 // Pattern used to generate endpoints for client. Fields filled 36 // %d - will be filled with member grpc port 37 // %s - will be filled with member name 38 clientURLPattern string 39 40 // Pattern used to validate authority received by server. Fields filled: 41 // %d - will be filled with first member grpc port 42 // %s - will be filled with first member name 43 expectAuthorityPattern string 44 }{ 45 { 46 name: "unix:path", 47 clientURLPattern: "unix:localhost:%s", 48 expectAuthorityPattern: "localhost:%s", 49 }, 50 { 51 name: "unix://absolute_path", 52 clientURLPattern: "unix://localhost:%s", 53 expectAuthorityPattern: "localhost:%s", 54 }, 55 // "unixs" is not standard schema supported by etcd 56 { 57 name: "unixs:absolute_path", 58 useTLS: true, 59 clientURLPattern: "unixs:localhost:%s", 60 expectAuthorityPattern: "localhost:%s", 61 }, 62 { 63 name: "unixs://absolute_path", 64 useTLS: true, 65 clientURLPattern: "unixs://localhost:%s", 66 expectAuthorityPattern: "localhost:%s", 67 }, 68 { 69 name: "http://domain[:port]", 70 useTCP: true, 71 clientURLPattern: "http://localhost:%d", 72 expectAuthorityPattern: "localhost:%d", 73 }, 74 { 75 name: "https://domain[:port]", 76 useTLS: true, 77 useTCP: true, 78 clientURLPattern: "https://localhost:%d", 79 expectAuthorityPattern: "localhost:%d", 80 }, 81 { 82 name: "http://address[:port]", 83 useTCP: true, 84 clientURLPattern: "http://127.0.0.1:%d", 85 expectAuthorityPattern: "127.0.0.1:%d", 86 }, 87 { 88 name: "https://address[:port]", 89 useTCP: true, 90 useTLS: true, 91 clientURLPattern: "https://127.0.0.1:%d", 92 expectAuthorityPattern: "127.0.0.1:%d", 93 }, 94 } 95 for _, tc := range tcs { 96 for _, clusterSize := range []int{1, 3} { 97 t.Run(fmt.Sprintf("Size: %d, Scenario: %q", clusterSize, tc.name), func(t *testing.T) { 98 integration.BeforeTest(t) 99 cfg := integration.ClusterConfig{ 100 Size: clusterSize, 101 UseTCP: tc.useTCP, 102 UseIP: tc.useTCP, 103 } 104 cfg, tlsConfig := setupTLS(t, tc.useTLS, cfg) 105 clus := integration.NewCluster(t, &cfg) 106 defer clus.Terminate(t) 107 108 kv := setupClient(t, tc.clientURLPattern, clus, tlsConfig) 109 defer kv.Close() 110 111 putRequestMethod := "/etcdserverpb.KV/Put" 112 _, err := kv.Put(context.TODO(), "foo", "bar") 113 if err != nil { 114 t.Fatal(err) 115 } 116 117 assertAuthority(t, templateAuthority(t, tc.expectAuthorityPattern, clus.Members[0]), clus, putRequestMethod) 118 }) 119 } 120 } 121 } 122 123 func setupTLS(t *testing.T, useTLS bool, cfg integration.ClusterConfig) (integration.ClusterConfig, *tls.Config) { 124 t.Helper() 125 if useTLS { 126 cfg.ClientTLS = &integration.TestTLSInfo 127 tlsConfig, err := integration.TestTLSInfo.ClientConfig() 128 if err != nil { 129 t.Fatal(err) 130 } 131 return cfg, tlsConfig 132 } 133 return cfg, nil 134 } 135 136 func setupClient(t *testing.T, endpointPattern string, clus *integration.Cluster, tlsConfig *tls.Config) *clientv3.Client { 137 t.Helper() 138 endpoints := templateEndpoints(t, endpointPattern, clus) 139 kv, err := clientv3.New(clientv3.Config{ 140 Endpoints: endpoints, 141 DialTimeout: 5 * time.Second, 142 DialOptions: []grpc.DialOption{grpc.WithBlock()}, 143 TLS: tlsConfig, 144 }) 145 if err != nil { 146 t.Fatal(err) 147 } 148 return kv 149 } 150 151 func templateEndpoints(t *testing.T, pattern string, clus *integration.Cluster) []string { 152 t.Helper() 153 var endpoints []string 154 for _, m := range clus.Members { 155 ent := pattern 156 if strings.Contains(ent, "%d") { 157 ent = fmt.Sprintf(ent, integration.GrpcPortNumber(m.UniqNumber, m.MemberNumber)) 158 } 159 if strings.Contains(ent, "%s") { 160 ent = fmt.Sprintf(ent, m.Name) 161 } 162 if strings.Contains(ent, "%") { 163 t.Fatalf("Failed to template pattern, %% symbol left %q", ent) 164 } 165 endpoints = append(endpoints, ent) 166 } 167 return endpoints 168 } 169 170 func templateAuthority(t *testing.T, pattern string, m *integration.Member) string { 171 t.Helper() 172 authority := pattern 173 if strings.Contains(authority, "%d") { 174 authority = fmt.Sprintf(authority, integration.GrpcPortNumber(m.UniqNumber, m.MemberNumber)) 175 } 176 if strings.Contains(authority, "%s") { 177 authority = fmt.Sprintf(authority, m.Name) 178 } 179 if strings.Contains(authority, "%") { 180 t.Fatalf("Failed to template pattern, %% symbol left %q", authority) 181 } 182 return authority 183 } 184 185 func assertAuthority(t *testing.T, expectedAuthority string, clus *integration.Cluster, filterMethod string) { 186 t.Helper() 187 requestsFound := 0 188 for _, m := range clus.Members { 189 for _, r := range m.RecordedRequests() { 190 if filterMethod != "" && r.FullMethod != filterMethod { 191 continue 192 } 193 requestsFound++ 194 if r.Authority != expectedAuthority { 195 t.Errorf("Got unexpected authority header, member: %q, request: %q, got authority: %q, expected %q", m.Name, r.FullMethod, r.Authority, expectedAuthority) 196 } 197 } 198 } 199 if requestsFound == 0 { 200 t.Errorf("Expected at least one request") 201 } 202 }