1 #region Copyright notice and license 2 3 // Copyright 2015 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 #endregion 18 19 using System; 20 using System.Collections.Generic; 21 using System.Linq; 22 using System.Threading; 23 using System.Threading.Tasks; 24 using Grpc.Core; 25 using Grpc.Core.Utils; 26 using Grpc.Testing; 27 using NUnit.Framework; 28 29 namespace Grpc.IntegrationTesting 30 { 31 public class XdsInteropClientTest 32 { 33 const string Host = "localhost"; 34 35 BackendServiceImpl backendService; 36 37 Server backendServer; 38 Server lbStatsServer; 39 Channel lbStatsChannel; 40 LoadBalancerStatsService.LoadBalancerStatsServiceClient lbStatsClient; 41 42 XdsInteropClient xdsInteropClient; 43 44 [OneTimeSetUp] Init()45 public void Init() 46 { 47 backendService = new BackendServiceImpl(); 48 49 // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 50 backendServer = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) 51 { 52 Services = { TestService.BindService(backendService) }, 53 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } 54 }; 55 backendServer.Start(); 56 57 xdsInteropClient = new XdsInteropClient(new XdsInteropClient.ClientOptions 58 { 59 NumChannels = 1, 60 Qps = 1, 61 RpcTimeoutSec = 10, 62 Rpc = "UnaryCall", 63 Server = $"{Host}:{backendServer.Ports.Single().BoundPort}", 64 }); 65 66 // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 67 lbStatsServer = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) 68 { 69 Services = { LoadBalancerStatsService.BindService(new LoadBalancerStatsServiceImpl(xdsInteropClient.StatsWatcher)) }, 70 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } 71 }; 72 lbStatsServer.Start(); 73 74 int port = lbStatsServer.Ports.Single().BoundPort; 75 lbStatsChannel = new Channel(Host, port, ChannelCredentials.Insecure); 76 lbStatsClient = new LoadBalancerStatsService.LoadBalancerStatsServiceClient(lbStatsChannel); 77 } 78 79 [OneTimeTearDown] Cleanup()80 public void Cleanup() 81 { 82 lbStatsChannel.ShutdownAsync().Wait(); 83 lbStatsServer.ShutdownAsync().Wait(); 84 backendServer.ShutdownAsync().Wait(); 85 } 86 87 [Test] SmokeTest()88 public async Task SmokeTest() 89 { 90 string backendName = "backend1"; 91 backendService.UnaryHandler = (request, context) => 92 { 93 return Task.FromResult(new SimpleResponse { Hostname = backendName }); 94 }; 95 96 var cancellationTokenSource = new CancellationTokenSource(); 97 var runChannelsTask = xdsInteropClient.RunChannelsAsync(cancellationTokenSource.Token); 98 99 var stats = await lbStatsClient.GetClientStatsAsync(new LoadBalancerStatsRequest 100 { 101 NumRpcs = 5, 102 TimeoutSec = 10, 103 }, deadline: DateTime.UtcNow.AddSeconds(30)); 104 105 Assert.AreEqual(0, stats.NumFailures); 106 Assert.AreEqual(backendName, stats.RpcsByPeer.Keys.Single()); 107 Assert.AreEqual(5, stats.RpcsByPeer[backendName]); 108 Assert.AreEqual("UnaryCall", stats.RpcsByMethod.Keys.Single()); 109 Assert.AreEqual(backendName, stats.RpcsByMethod["UnaryCall"].RpcsByPeer_.Keys.Single()); 110 Assert.AreEqual(5, stats.RpcsByMethod["UnaryCall"].RpcsByPeer_[backendName]); 111 112 await Task.Delay(100); 113 114 var stats2 = await lbStatsClient.GetClientStatsAsync(new LoadBalancerStatsRequest 115 { 116 NumRpcs = 3, 117 TimeoutSec = 10, 118 }, deadline: DateTime.UtcNow.AddSeconds(30)); 119 120 Assert.AreEqual(0, stats2.NumFailures); 121 Assert.AreEqual(backendName, stats2.RpcsByPeer.Keys.Single()); 122 Assert.AreEqual(3, stats2.RpcsByPeer[backendName]); 123 Assert.AreEqual("UnaryCall", stats2.RpcsByMethod.Keys.Single()); 124 Assert.AreEqual(backendName, stats2.RpcsByMethod["UnaryCall"].RpcsByPeer_.Keys.Single()); 125 Assert.AreEqual(3, stats2.RpcsByMethod["UnaryCall"].RpcsByPeer_[backendName]); 126 127 cancellationTokenSource.Cancel(); 128 await runChannelsTask; 129 } 130 131 [Test] HostnameReadFromResponseHeaders()132 public async Task HostnameReadFromResponseHeaders() 133 { 134 string correctBackendName = "backend1"; 135 backendService.UnaryHandler = async (request, context) => 136 { 137 await context.WriteResponseHeadersAsync(new Metadata { {"hostname", correctBackendName} }); 138 return new SimpleResponse { Hostname = "wrong_hostname" }; 139 }; 140 141 var cancellationTokenSource = new CancellationTokenSource(); 142 var runChannelsTask = xdsInteropClient.RunChannelsAsync(cancellationTokenSource.Token); 143 144 var stats = await lbStatsClient.GetClientStatsAsync(new LoadBalancerStatsRequest 145 { 146 NumRpcs = 3, 147 TimeoutSec = 10, 148 }, deadline: DateTime.UtcNow.AddSeconds(30)); 149 150 Assert.AreEqual(0, stats.NumFailures); 151 Assert.AreEqual(correctBackendName, stats.RpcsByPeer.Keys.Single()); 152 Assert.AreEqual(3, stats.RpcsByPeer[correctBackendName]); 153 154 cancellationTokenSource.Cancel(); 155 await runChannelsTask; 156 } 157 158 public class BackendServiceImpl : TestService.TestServiceBase 159 { 160 public UnaryServerMethod<SimpleRequest, SimpleResponse> UnaryHandler { get; set; } 161 public UnaryServerMethod<Empty, Empty> EmptyHandler { get; set; } 162 UnaryCall(SimpleRequest request, ServerCallContext context)163 public override Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context) 164 { 165 return UnaryHandler(request, context); 166 } 167 EmptyCall(Empty request, ServerCallContext context)168 public override Task<Empty> EmptyCall(Empty request, ServerCallContext context) 169 { 170 return EmptyHandler(request, context); 171 } 172 } 173 } 174 } 175