google.golang.org/grpc@v1.74.2/xds/internal/clients/xdsclient/test/authority_test.go (about) 1 /* 2 * 3 * Copyright 2022 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 xdsclient_test 20 21 import ( 22 "context" 23 "net" 24 "testing" 25 26 "github.com/google/uuid" 27 "google.golang.org/grpc/credentials/insecure" 28 "google.golang.org/grpc/xds/internal/clients" 29 "google.golang.org/grpc/xds/internal/clients/grpctransport" 30 "google.golang.org/grpc/xds/internal/clients/internal/testutils" 31 "google.golang.org/grpc/xds/internal/clients/internal/testutils/e2e" 32 "google.golang.org/grpc/xds/internal/clients/xdsclient" 33 "google.golang.org/grpc/xds/internal/clients/xdsclient/internal/xdsresource" 34 35 v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 36 ) 37 38 const ( 39 testAuthority1 = "test-authority1" 40 testAuthority2 = "test-authority2" 41 testAuthority3 = "test-authority3" 42 ) 43 44 var ( 45 // These two resources use `testAuthority1`, which contains an empty server 46 // config and therefore will use the default management server. 47 authorityTestResourceName11 = buildResourceName(listenerResourceTypeName, testAuthority1, ldsName, nil) 48 authorityTestResourceName12 = buildResourceName(listenerResourceTypeName, testAuthority1, ldsName+"2", nil) 49 // This resource uses `testAuthority2`, which contains an empty server 50 // config and therefore will use the default management server. 51 authorityTestResourceName2 = buildResourceName(listenerResourceTypeName, testAuthority2, ldsName+"3", nil) 52 // This resource uses `testAuthority3`, which contains a non-empty server 53 // config, and therefore will use the non-default management server. 54 authorityTestResourceName3 = buildResourceName(listenerResourceTypeName, testAuthority3, ldsName+"3", nil) 55 ) 56 57 // setupForAuthorityTests spins up two management servers, one to act as the 58 // default and the other to act as the non-default. It also creates a 59 // xDS client configuration with three authorities (the first two pointing to 60 // the default and the third one pointing to the non-default). 61 // 62 // Returns two listeners used by the default and non-default management servers 63 // respectively, and the xDS client. 64 func setupForAuthorityTests(ctx context.Context, t *testing.T) (*testutils.ListenerWrapper, *testutils.ListenerWrapper, *xdsclient.XDSClient) { 65 // Create listener wrappers which notify on to a channel whenever a new 66 // connection is accepted. We use this to track the number of transports 67 // used by the xDS client. 68 lisDefault := testutils.NewListenerWrapper(t, nil) 69 lisNonDefault := testutils.NewListenerWrapper(t, nil) 70 71 // Start a management server to act as the default authority. 72 defaultAuthorityServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{Listener: lisDefault}) 73 74 // Start a management server to act as the non-default authority. 75 nonDefaultAuthorityServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{Listener: lisNonDefault}) 76 77 // Create a bootstrap configuration with two non-default authorities which 78 // have empty server configs, and therefore end up using the default server 79 // config, which points to the above management server. 80 nodeID := uuid.New().String() 81 82 resourceTypes := map[string]xdsclient.ResourceType{xdsresource.V3ListenerURL: listenerType} 83 ext := grpctransport.ServerIdentifierExtension{ConfigName: "insecure"} 84 siDefault := clients.ServerIdentifier{ 85 ServerURI: defaultAuthorityServer.Address, 86 Extensions: ext, 87 } 88 siNonDefault := clients.ServerIdentifier{ 89 ServerURI: nonDefaultAuthorityServer.Address, 90 Extensions: ext, 91 } 92 93 configs := map[string]grpctransport.Config{"insecure": {Credentials: insecure.NewBundle()}} 94 xdsClientConfig := xdsclient.Config{ 95 Servers: []xdsclient.ServerConfig{{ServerIdentifier: siDefault}}, 96 Node: clients.Node{ID: nodeID}, 97 TransportBuilder: grpctransport.NewBuilder(configs), 98 ResourceTypes: resourceTypes, 99 // Xdstp style resource names used in this test use a slash removed 100 // version of t.Name as their authority, and the empty config 101 // results in the top-level xds server configuration being used for 102 // this authority. 103 Authorities: map[string]xdsclient.Authority{ 104 testAuthority1: {XDSServers: []xdsclient.ServerConfig{}}, 105 testAuthority2: {XDSServers: []xdsclient.ServerConfig{}}, 106 testAuthority3: {XDSServers: []xdsclient.ServerConfig{{ServerIdentifier: siNonDefault}}}, 107 }, 108 } 109 110 // Create an xDS client with the above config. 111 overrideWatchExpiryTimeout(t, defaultTestWatchExpiryTimeout) 112 client, err := xdsclient.New(xdsClientConfig) 113 if err != nil { 114 t.Fatalf("Failed to create xDS client: %v", err) 115 } 116 117 resources := e2e.UpdateOptions{ 118 NodeID: nodeID, 119 Listeners: []*v3listenerpb.Listener{ 120 e2e.DefaultClientListener(authorityTestResourceName11, rdsName), 121 e2e.DefaultClientListener(authorityTestResourceName12, rdsName), 122 e2e.DefaultClientListener(authorityTestResourceName2, rdsName), 123 e2e.DefaultClientListener(authorityTestResourceName3, rdsName), 124 }, 125 SkipValidation: true, 126 } 127 if err := defaultAuthorityServer.Update(ctx, resources); err != nil { 128 t.Fatalf("Failed to update management server with resources: %v, err: %v", resources, err) 129 } 130 return lisDefault, lisNonDefault, client 131 } 132 133 // Tests the xdsChannel sharing logic among authorities. The test verifies the 134 // following scenarios: 135 // - A watch for a resource name with an authority matching an existing watch 136 // should not result in a new transport being created. 137 // - A watch for a resource name with different authority name but same 138 // authority config as an existing watch should not result in a new transport 139 // being created. 140 func (s) TestAuthority_XDSChannelSharing(t *testing.T) { 141 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 142 defer cancel() 143 lis, _, client := setupForAuthorityTests(ctx, t) 144 defer client.Close() 145 146 // Verify that no connection is established to the management server at this 147 // point. A transport is created only when a resource (which belongs to that 148 // authority) is requested. 149 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 150 defer sCancel() 151 if _, err := lis.NewConnCh.Receive(sCtx); err != context.DeadlineExceeded { 152 t.Fatal("Unexpected new transport created to management server") 153 } 154 155 // Request the first resource. Verify that a new transport is created. 156 watcher := noopListenerWatcher{} 157 ldsCancel1 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName11, watcher) 158 defer ldsCancel1() 159 if _, err := lis.NewConnCh.Receive(ctx); err != nil { 160 t.Fatalf("Timed out when waiting for a new transport to be created to the management server: %v", err) 161 } 162 163 // Request the second resource. Verify that no new transport is created. 164 ldsCancel2 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName12, watcher) 165 defer ldsCancel2() 166 sCtx, sCancel = context.WithTimeout(ctx, defaultTestShortTimeout) 167 defer sCancel() 168 if _, err := lis.NewConnCh.Receive(sCtx); err != context.DeadlineExceeded { 169 t.Fatal("Unexpected new transport created to management server") 170 } 171 172 // Request the third resource. Verify that no new transport is created. 173 ldsCancel3 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName2, watcher) 174 defer ldsCancel3() 175 sCtx, sCancel = context.WithTimeout(ctx, defaultTestShortTimeout) 176 defer sCancel() 177 if _, err := lis.NewConnCh.Receive(sCtx); err != context.DeadlineExceeded { 178 t.Fatal("Unexpected new transport created to management server") 179 } 180 } 181 182 // Test the xdsChannel close logic. The test verifies that the xDS client 183 // closes an xdsChannel immediately after the last watch is canceled. 184 func (s) TestAuthority_XDSChannelClose(t *testing.T) { 185 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 186 defer cancel() 187 lis, _, client := setupForAuthorityTests(ctx, t) 188 defer client.Close() 189 190 // Request the first resource. Verify that a new transport is created. 191 watcher := noopListenerWatcher{} 192 ldsCancel1 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName11, watcher) 193 val, err := lis.NewConnCh.Receive(ctx) 194 if err != nil { 195 t.Fatalf("Timed out when waiting for a new transport to be created to the management server: %v", err) 196 } 197 conn := val.(*testutils.ConnWrapper) 198 199 // Request the second resource. Verify that no new transport is created. 200 ldsCancel2 := client.WatchResource(xdsresource.V3ListenerURL, authorityTestResourceName12, watcher) 201 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 202 defer sCancel() 203 if _, err := lis.NewConnCh.Receive(sCtx); err != context.DeadlineExceeded { 204 t.Fatal("Unexpected new transport created to management server") 205 } 206 207 // Cancel both watches, and verify that the connection to the management 208 // server is closed. 209 ldsCancel1() 210 ldsCancel2() 211 if _, err := conn.CloseCh.Receive(ctx); err != nil { 212 t.Fatal("Timeout when waiting for connection to management server to be closed") 213 } 214 } 215 216 // Tests the scenario where the primary management server is unavailable at 217 // startup and the xDS client falls back to the secondary. The test verifies 218 // that the resource watcher is not notified of the connectivity failure until 219 // all servers have failed. 220 func (s) TestAuthority_Fallback(t *testing.T) { 221 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 222 defer cancel() 223 224 // Create primary and secondary management servers with restartable 225 // listeners. 226 l, err := net.Listen("tcp", "localhost:0") 227 if err != nil { 228 t.Fatalf("net.Listen() failed: %v", err) 229 } 230 primaryLis := testutils.NewRestartableListener(l) 231 primaryMgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{Listener: primaryLis}) 232 l, err = net.Listen("tcp", "localhost:0") 233 if err != nil { 234 t.Fatalf("net.Listen() failed: %v", err) 235 } 236 secondaryLis := testutils.NewRestartableListener(l) 237 secondaryMgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{Listener: secondaryLis}) 238 239 nodeID := uuid.New().String() 240 241 resourceTypes := map[string]xdsclient.ResourceType{xdsresource.V3ListenerURL: listenerType} 242 psi := clients.ServerIdentifier{ 243 ServerURI: primaryMgmtServer.Address, 244 Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}, 245 } 246 ssi := clients.ServerIdentifier{ 247 ServerURI: secondaryMgmtServer.Address, 248 Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}, 249 } 250 251 // Create config with the above primary and fallback management servers, 252 // and an xDS client with that configuration. 253 configs := map[string]grpctransport.Config{"insecure": {Credentials: insecure.NewBundle()}} 254 xdsClientConfig := xdsclient.Config{ 255 Servers: []xdsclient.ServerConfig{{ServerIdentifier: psi}, {ServerIdentifier: ssi}}, 256 Node: clients.Node{ID: nodeID}, 257 TransportBuilder: grpctransport.NewBuilder(configs), 258 ResourceTypes: resourceTypes, 259 // Xdstp resource names used in this test do not specify an 260 // authority. These will end up looking up an entry with the 261 // empty key in the authorities map. Having an entry with an 262 // empty key and empty configuration, results in these 263 // resources also using the top-level configuration. 264 Authorities: map[string]xdsclient.Authority{ 265 "": {XDSServers: []xdsclient.ServerConfig{}}, 266 }, 267 } 268 269 // Create an xDS client with the above config. 270 client, err := xdsclient.New(xdsClientConfig) 271 if err != nil { 272 t.Fatalf("Failed to create xDS client: %v", err) 273 } 274 defer client.Close() 275 276 const listenerName = "listener" 277 const rdsPrimaryName = "rds-primary" 278 const rdsSecondaryName = "rds-secondary" 279 280 // Create a Cluster resource on the primary. 281 resources := e2e.UpdateOptions{ 282 NodeID: nodeID, 283 Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(listenerName, rdsPrimaryName)}, 284 SkipValidation: true, 285 } 286 if err := primaryMgmtServer.Update(ctx, resources); err != nil { 287 t.Fatalf("Failed to update primary management server with resources: %v, err: %v", resources, err) 288 } 289 290 // Create a Cluster resource on the secondary . 291 resources = e2e.UpdateOptions{ 292 NodeID: nodeID, 293 Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(listenerName, rdsSecondaryName)}, 294 SkipValidation: true, 295 } 296 if err := secondaryMgmtServer.Update(ctx, resources); err != nil { 297 t.Fatalf("Failed to update primary management server with resources: %v, err: %v", resources, err) 298 } 299 300 // Stop the primary. 301 primaryLis.Close() 302 303 // Register a watch. 304 watcher := newListenerWatcher() 305 ldsCancel := client.WatchResource(xdsresource.V3ListenerURL, listenerName, watcher) 306 defer ldsCancel() 307 308 // Ensure that the connectivity error callback is not called. Since, this 309 // is the first watch without cached resource, it checks for resourceErrCh 310 sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout) 311 defer sCancel() 312 if v, err := watcher.resourceErrCh.Receive(sCtx); err != context.DeadlineExceeded { 313 t.Fatalf("Resource error callback on the watcher with error: %v", v.(error)) 314 } 315 316 // Ensure that the resource update callback is invoked. 317 wantUpdate := listenerUpdateErrTuple{ 318 update: listenerUpdate{ 319 RouteConfigName: rdsSecondaryName, 320 }, 321 } 322 if err := verifyListenerUpdate(ctx, watcher.updateCh, wantUpdate); err != nil { 323 t.Fatal(err) 324 } 325 326 // Stop the secondary. 327 secondaryLis.Close() 328 329 // Ensure that the connectivity error callback is called as ambient error 330 // since cached resource exist. 331 if _, err := watcher.ambientErrCh.Receive(ctx); err != nil { 332 t.Fatal("Timeout when waiting for ambient error callback on the watcher") 333 } 334 }