mirror of https://github.com/grpc/grpc.git
commit
12821db7f8
29 changed files with 1097 additions and 48 deletions
@ -0,0 +1,3 @@ |
||||
bin |
||||
obj |
||||
|
@ -0,0 +1,31 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2015 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System; |
||||
using Grpc.IntegrationTesting; |
||||
|
||||
namespace Grpc.IntegrationTesting.XdsClient |
||||
{ |
||||
class Program |
||||
{ |
||||
public static void Main(string[] args) |
||||
{ |
||||
XdsInteropClient.Run(args); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,29 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2015 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System.Reflection; |
||||
using System.Runtime.CompilerServices; |
||||
|
||||
[assembly: AssemblyTitle("Grpc.IntegrationTesting.XdsClient")] |
||||
[assembly: AssemblyDescription("")] |
||||
[assembly: AssemblyConfiguration("")] |
||||
[assembly: AssemblyCompany("")] |
||||
[assembly: AssemblyProduct("")] |
||||
[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] |
||||
[assembly: AssemblyTrademark("")] |
||||
[assembly: AssemblyCulture("")] |
@ -0,0 +1,300 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2020 The gRPC Authors |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
|
||||
using CommandLine; |
||||
using Grpc.Core; |
||||
using Grpc.Core.Logging; |
||||
using Grpc.Core.Internal; |
||||
using Grpc.Testing; |
||||
|
||||
namespace Grpc.IntegrationTesting |
||||
{ |
||||
public class XdsInteropClient |
||||
{ |
||||
internal class ClientOptions |
||||
{ |
||||
[Option("num_channels", Default = 1)] |
||||
public int NumChannels { get; set; } |
||||
|
||||
[Option("qps", Default = 1)] |
||||
|
||||
// The desired QPS per channel. |
||||
public int Qps { get; set; } |
||||
|
||||
[Option("server", Default = "localhost:8080")] |
||||
public string Server { get; set; } |
||||
|
||||
[Option("stats_port", Default = 8081)] |
||||
public int StatsPort { get; set; } |
||||
|
||||
[Option("rpc_timeout_sec", Default = 30)] |
||||
public int RpcTimeoutSec { get; set; } |
||||
|
||||
[Option("print_response", Default = false)] |
||||
public bool PrintResponse { get; set; } |
||||
} |
||||
|
||||
ClientOptions options; |
||||
|
||||
StatsWatcher statsWatcher = new StatsWatcher(); |
||||
|
||||
// make watcher accessible by tests |
||||
internal StatsWatcher StatsWatcher => statsWatcher; |
||||
|
||||
internal XdsInteropClient(ClientOptions options) |
||||
{ |
||||
this.options = options; |
||||
} |
||||
|
||||
public static void Run(string[] args) |
||||
{ |
||||
GrpcEnvironment.SetLogger(new ConsoleLogger()); |
||||
var parserResult = Parser.Default.ParseArguments<ClientOptions>(args) |
||||
.WithNotParsed(errors => Environment.Exit(1)) |
||||
.WithParsed(options => |
||||
{ |
||||
var xdsInteropClient = new XdsInteropClient(options); |
||||
xdsInteropClient.RunAsync().Wait(); |
||||
}); |
||||
} |
||||
|
||||
private async Task RunAsync() |
||||
{ |
||||
var server = new Server |
||||
{ |
||||
Services = { LoadBalancerStatsService.BindService(new LoadBalancerStatsServiceImpl(statsWatcher)) } |
||||
}; |
||||
|
||||
string host = "0.0.0.0"; |
||||
server.Ports.Add(host, options.StatsPort, ServerCredentials.Insecure); |
||||
Console.WriteLine($"Running server on {host}:{options.StatsPort}"); |
||||
server.Start(); |
||||
|
||||
var cancellationTokenSource = new CancellationTokenSource(); |
||||
await RunChannelsAsync(cancellationTokenSource.Token); |
||||
|
||||
await server.ShutdownAsync(); |
||||
} |
||||
|
||||
// method made internal to make it runnable by tests |
||||
internal async Task RunChannelsAsync(CancellationToken cancellationToken) |
||||
{ |
||||
var channelTasks = new List<Task>(); |
||||
for (int channelId = 0; channelId < options.NumChannels; channelId++) |
||||
{ |
||||
var channelTask = RunSingleChannelAsync(channelId, cancellationToken); |
||||
channelTasks.Add(channelTask); |
||||
} |
||||
|
||||
for (int channelId = 0; channelId < options.NumChannels; channelId++) |
||||
{ |
||||
await channelTasks[channelId]; |
||||
} |
||||
} |
||||
|
||||
private async Task RunSingleChannelAsync(int channelId, CancellationToken cancellationToken) |
||||
{ |
||||
Console.WriteLine($"Starting channel {channelId}"); |
||||
var channel = new Channel(options.Server, ChannelCredentials.Insecure); |
||||
var client = new TestService.TestServiceClient(channel); |
||||
|
||||
var inflightTasks = new List<Task>(); |
||||
long rpcsStarted = 0; |
||||
var stopwatch = Stopwatch.StartNew(); |
||||
while (!cancellationToken.IsCancellationRequested) |
||||
{ |
||||
inflightTasks.Add(RunSingleRpcAsync(client, cancellationToken)); |
||||
rpcsStarted++; |
||||
|
||||
// only cleanup calls that have already completed, calls that are still inflight will be cleaned up later. |
||||
await CleanupCompletedTasksAsync(inflightTasks); |
||||
|
||||
Console.WriteLine($"Currently {inflightTasks.Count} in-flight RPCs"); |
||||
|
||||
// if needed, wait a bit before we start the next RPC. |
||||
int nextDueInMillis = (int) Math.Max(0, (1000 * rpcsStarted / options.Qps) - stopwatch.ElapsedMilliseconds); |
||||
if (nextDueInMillis > 0) |
||||
{ |
||||
await Task.Delay(nextDueInMillis); |
||||
} |
||||
} |
||||
stopwatch.Stop(); |
||||
|
||||
Console.WriteLine($"Shutting down channel {channelId}"); |
||||
await channel.ShutdownAsync(); |
||||
Console.WriteLine($"Channel shutdown {channelId}"); |
||||
} |
||||
|
||||
private async Task RunSingleRpcAsync(TestService.TestServiceClient client, CancellationToken cancellationToken) |
||||
{ |
||||
long rpcId = statsWatcher.RpcIdGenerator.Increment(); |
||||
try |
||||
{ |
||||
Console.WriteLine($"Starting RPC {rpcId}."); |
||||
var response = await client.UnaryCallAsync(new SimpleRequest(), |
||||
new CallOptions(cancellationToken: cancellationToken, deadline: DateTime.UtcNow.AddSeconds(options.RpcTimeoutSec))); |
||||
|
||||
statsWatcher.OnRpcComplete(rpcId, response.Hostname); |
||||
if (options.PrintResponse) |
||||
{ |
||||
Console.WriteLine($"Got response {response}"); |
||||
} |
||||
Console.WriteLine($"RPC {rpcId} succeeded "); |
||||
} |
||||
catch (RpcException ex) |
||||
{ |
||||
statsWatcher.OnRpcComplete(rpcId, null); |
||||
Console.WriteLine($"RPC {rpcId} failed: {ex}"); |
||||
} |
||||
} |
||||
|
||||
private async Task CleanupCompletedTasksAsync(List<Task> tasks) |
||||
{ |
||||
var toRemove = new List<Task>(); |
||||
foreach (var task in tasks) |
||||
{ |
||||
if (task.IsCompleted) |
||||
{ |
||||
// awaiting tasks that have already completed should be instantaneous |
||||
await task; |
||||
} |
||||
toRemove.Add(task); |
||||
} |
||||
foreach (var task in toRemove) |
||||
{ |
||||
tasks.Remove(task); |
||||
} |
||||
} |
||||
} |
||||
|
||||
internal class StatsWatcher |
||||
{ |
||||
private readonly object myLock = new object(); |
||||
private readonly AtomicCounter rpcIdGenerator = new AtomicCounter(0); |
||||
|
||||
private long? firstAcceptedRpcId; |
||||
private int numRpcsWanted; |
||||
private int rpcsCompleted; |
||||
private int rpcsNoHostname; |
||||
private Dictionary<string, int> rpcsByHostname; |
||||
|
||||
public AtomicCounter RpcIdGenerator => rpcIdGenerator; |
||||
|
||||
public StatsWatcher() |
||||
{ |
||||
Reset(); |
||||
} |
||||
|
||||
public void OnRpcComplete(long rpcId, string responseHostname) |
||||
{ |
||||
lock (myLock) |
||||
{ |
||||
if (!firstAcceptedRpcId.HasValue || rpcId < firstAcceptedRpcId || rpcId >= firstAcceptedRpcId + numRpcsWanted) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
if (string.IsNullOrEmpty(responseHostname)) |
||||
{ |
||||
rpcsNoHostname ++; |
||||
} |
||||
else |
||||
{ |
||||
if (!rpcsByHostname.ContainsKey(responseHostname)) |
||||
{ |
||||
rpcsByHostname[responseHostname] = 0; |
||||
} |
||||
rpcsByHostname[responseHostname] += 1; |
||||
} |
||||
rpcsCompleted += 1; |
||||
|
||||
if (rpcsCompleted >= numRpcsWanted) |
||||
{ |
||||
Monitor.Pulse(myLock); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void Reset() |
||||
{ |
||||
lock (myLock) |
||||
{ |
||||
firstAcceptedRpcId = null; |
||||
numRpcsWanted = 0; |
||||
rpcsCompleted = 0; |
||||
rpcsNoHostname = 0; |
||||
rpcsByHostname = new Dictionary<string, int>(); |
||||
} |
||||
} |
||||
|
||||
public LoadBalancerStatsResponse WaitForRpcStatsResponse(int rpcsWanted, int timeoutSec) |
||||
{ |
||||
lock (myLock) |
||||
{ |
||||
if (firstAcceptedRpcId.HasValue) |
||||
{ |
||||
throw new InvalidOperationException("StateWatcher is already collecting stats."); |
||||
} |
||||
// we are only interested in the next numRpcsWanted RPCs |
||||
firstAcceptedRpcId = rpcIdGenerator.Count + 1; |
||||
numRpcsWanted = rpcsWanted; |
||||
|
||||
var deadline = DateTime.UtcNow.AddSeconds(timeoutSec); |
||||
while (true) |
||||
{ |
||||
var timeoutMillis = Math.Max((int)(deadline - DateTime.UtcNow).TotalMilliseconds, 0); |
||||
if (!Monitor.Wait(myLock, timeoutMillis) || rpcsCompleted >= rpcsWanted) |
||||
{ |
||||
// we collected enough RPCs, or timed out waiting |
||||
var response = new LoadBalancerStatsResponse { NumFailures = rpcsNoHostname }; |
||||
response.RpcsByPeer.Add(rpcsByHostname); |
||||
Reset(); |
||||
return response; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Implementation of LoadBalancerStatsService server |
||||
/// </summary> |
||||
internal class LoadBalancerStatsServiceImpl : LoadBalancerStatsService.LoadBalancerStatsServiceBase |
||||
{ |
||||
StatsWatcher statsWatcher; |
||||
|
||||
public LoadBalancerStatsServiceImpl(StatsWatcher statsWatcher) |
||||
{ |
||||
this.statsWatcher = statsWatcher; |
||||
} |
||||
|
||||
public override async Task<LoadBalancerStatsResponse> GetClientStats(LoadBalancerStatsRequest request, ServerCallContext context) |
||||
{ |
||||
// run as a task to avoid blocking |
||||
var response = await Task.Run(() => statsWatcher.WaitForRpcStatsResponse(request.NumRpcs, request.TimeoutSec)); |
||||
Console.WriteLine($"Returning stats {response} (num of requested RPCs: {request.NumRpcs})"); |
||||
return response; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,134 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2015 gRPC authors. |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using Grpc.Core; |
||||
using Grpc.Core.Utils; |
||||
using Grpc.Testing; |
||||
using NUnit.Framework; |
||||
|
||||
namespace Grpc.IntegrationTesting |
||||
{ |
||||
public class XdsInteropClientTest |
||||
{ |
||||
const string Host = "localhost"; |
||||
|
||||
BackendServiceImpl backendService; |
||||
|
||||
Server backendServer; |
||||
Server lbStatsServer; |
||||
Channel lbStatsChannel; |
||||
LoadBalancerStatsService.LoadBalancerStatsServiceClient lbStatsClient; |
||||
|
||||
XdsInteropClient xdsInteropClient; |
||||
|
||||
[OneTimeSetUp] |
||||
public void Init() |
||||
{ |
||||
backendService = new BackendServiceImpl(); |
||||
|
||||
// Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 |
||||
backendServer = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) |
||||
{ |
||||
Services = { TestService.BindService(backendService) }, |
||||
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } |
||||
}; |
||||
backendServer.Start(); |
||||
|
||||
xdsInteropClient = new XdsInteropClient(new XdsInteropClient.ClientOptions |
||||
{ |
||||
NumChannels = 1, |
||||
Qps = 1, |
||||
RpcTimeoutSec = 10, |
||||
Server = $"{Host}:{backendServer.Ports.Single().BoundPort}", |
||||
}); |
||||
|
||||
// Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 |
||||
lbStatsServer = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) |
||||
{ |
||||
Services = { LoadBalancerStatsService.BindService(new LoadBalancerStatsServiceImpl(xdsInteropClient.StatsWatcher)) }, |
||||
Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } |
||||
}; |
||||
lbStatsServer.Start(); |
||||
|
||||
int port = lbStatsServer.Ports.Single().BoundPort; |
||||
lbStatsChannel = new Channel(Host, port, ChannelCredentials.Insecure); |
||||
lbStatsClient = new LoadBalancerStatsService.LoadBalancerStatsServiceClient(lbStatsChannel); |
||||
} |
||||
|
||||
[OneTimeTearDown] |
||||
public void Cleanup() |
||||
{ |
||||
lbStatsChannel.ShutdownAsync().Wait(); |
||||
lbStatsServer.ShutdownAsync().Wait(); |
||||
backendServer.ShutdownAsync().Wait(); |
||||
} |
||||
|
||||
[Test] |
||||
public async Task SmokeTest() |
||||
{ |
||||
string backendName = "backend1"; |
||||
backendService.UnaryHandler = (request, context) => |
||||
{ |
||||
return Task.FromResult(new SimpleResponse { Hostname = backendName}); |
||||
}; |
||||
|
||||
var cancellationTokenSource = new CancellationTokenSource(); |
||||
var runChannelsTask = xdsInteropClient.RunChannelsAsync(cancellationTokenSource.Token); |
||||
|
||||
var stats = await lbStatsClient.GetClientStatsAsync(new LoadBalancerStatsRequest |
||||
{ |
||||
NumRpcs = 5, |
||||
TimeoutSec = 10, |
||||
}, deadline: DateTime.UtcNow.AddSeconds(30)); |
||||
|
||||
Assert.AreEqual(0, stats.NumFailures); |
||||
Assert.AreEqual(backendName, stats.RpcsByPeer.Keys.Single()); |
||||
Assert.AreEqual(5, stats.RpcsByPeer[backendName]); |
||||
|
||||
await Task.Delay(100); |
||||
|
||||
var stats2 = await lbStatsClient.GetClientStatsAsync(new LoadBalancerStatsRequest |
||||
{ |
||||
NumRpcs = 3, |
||||
TimeoutSec = 10, |
||||
}, deadline: DateTime.UtcNow.AddSeconds(30)); |
||||
|
||||
Assert.AreEqual(0, stats2.NumFailures); |
||||
Assert.AreEqual(backendName, stats2.RpcsByPeer.Keys.Single()); |
||||
Assert.AreEqual(3, stats2.RpcsByPeer[backendName]); |
||||
|
||||
cancellationTokenSource.Cancel(); |
||||
await runChannelsTask; |
||||
} |
||||
|
||||
public class BackendServiceImpl : TestService.TestServiceBase |
||||
{ |
||||
public UnaryServerMethod<SimpleRequest, SimpleResponse> UnaryHandler { get; set; } |
||||
|
||||
public override Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context) |
||||
{ |
||||
return UnaryHandler(request, context); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,208 @@ |
||||
#!/usr/bin/env ruby |
||||
|
||||
# Copyright 2015 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
# This is the xDS interop test Ruby client. This is meant to be run by |
||||
# the run_xds_tests.py test runner. |
||||
# |
||||
# Usage: $ tools/run_tests/run_xds_tests.py --test_case=... ... |
||||
# --client_cmd="path/to/xds_client.rb --server=<hostname> \ |
||||
# --stats_port=<port> \ |
||||
# --qps=<qps>" |
||||
|
||||
# These lines are required for the generated files to load grpc |
||||
this_dir = File.expand_path(File.dirname(__FILE__)) |
||||
lib_dir = File.join(File.dirname(File.dirname(this_dir)), 'lib') |
||||
pb_dir = File.dirname(this_dir) |
||||
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir) |
||||
$LOAD_PATH.unshift(pb_dir) unless $LOAD_PATH.include?(pb_dir) |
||||
|
||||
require 'optparse' |
||||
require 'logger' |
||||
|
||||
require_relative '../../lib/grpc' |
||||
require 'google/protobuf' |
||||
|
||||
require_relative '../src/proto/grpc/testing/empty_pb' |
||||
require_relative '../src/proto/grpc/testing/messages_pb' |
||||
require_relative '../src/proto/grpc/testing/test_services_pb' |
||||
|
||||
# Some global variables to be shared by server and client |
||||
$watchers = Array.new |
||||
$watchers_mutex = Mutex.new |
||||
$watchers_cv = ConditionVariable.new |
||||
$shutdown = false |
||||
|
||||
# RubyLogger defines a logger for gRPC based on the standard ruby logger. |
||||
module RubyLogger |
||||
def logger |
||||
LOGGER |
||||
end |
||||
|
||||
LOGGER = Logger.new(STDOUT) |
||||
LOGGER.level = Logger::INFO |
||||
end |
||||
|
||||
# GRPC is the general RPC module |
||||
module GRPC |
||||
# Inject the noop #logger if no module-level logger method has been injected. |
||||
extend RubyLogger |
||||
end |
||||
|
||||
# creates a test stub |
||||
def create_stub(opts) |
||||
address = "#{opts.server}" |
||||
GRPC.logger.info("... connecting insecurely to #{address}") |
||||
Grpc::Testing::TestService::Stub.new( |
||||
address, |
||||
:this_channel_is_insecure, |
||||
) |
||||
end |
||||
|
||||
# This implements LoadBalancerStatsService required by the test runner |
||||
class TestTarget < Grpc::Testing::LoadBalancerStatsService::Service |
||||
include Grpc::Testing |
||||
|
||||
def get_client_stats(req, _call) |
||||
finish_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + |
||||
req['timeout_sec'] |
||||
watcher = {} |
||||
$watchers_mutex.synchronize do |
||||
watcher = { |
||||
"rpcs_by_peer" => Hash.new(0), |
||||
"rpcs_needed" => req['num_rpcs'], |
||||
"no_remote_peer" => 0 |
||||
} |
||||
$watchers << watcher |
||||
seconds_remaining = finish_time - |
||||
Process.clock_gettime(Process::CLOCK_MONOTONIC) |
||||
while watcher['rpcs_needed'] > 0 && seconds_remaining > 0 |
||||
$watchers_cv.wait($watchers_mutex, seconds_remaining) |
||||
seconds_remaining = finish_time - |
||||
Process.clock_gettime(Process::CLOCK_MONOTONIC) |
||||
end |
||||
$watchers.delete_at($watchers.index(watcher)) |
||||
end |
||||
LoadBalancerStatsResponse.new( |
||||
rpcs_by_peer: watcher['rpcs_by_peer'], |
||||
num_failures: watcher['no_remote_peer'] + watcher['rpcs_needed'] |
||||
); |
||||
end |
||||
end |
||||
|
||||
# send 1 rpc every 1/qps second |
||||
def run_test_loop(stub, target_seconds_between_rpcs, fail_on_failed_rpcs) |
||||
include Grpc::Testing |
||||
req = SimpleRequest.new() |
||||
target_next_start = Process.clock_gettime(Process::CLOCK_MONOTONIC) |
||||
while !$shutdown |
||||
now = Process.clock_gettime(Process::CLOCK_MONOTONIC) |
||||
sleep_seconds = target_next_start - now |
||||
if sleep_seconds < 0 |
||||
GRPC.logger.info("ruby xds: warning, rpc takes too long to finish. " \ |
||||
"If you consistently see this, the qps is too high.") |
||||
else |
||||
sleep(sleep_seconds) |
||||
end |
||||
target_next_start += target_seconds_between_rpcs |
||||
begin |
||||
resp = stub.unary_call(req) |
||||
remote_peer = resp.hostname |
||||
rescue GRPC::BadStatus => e |
||||
remote_peer = "" |
||||
GRPC.logger.info("ruby xds: rpc failed:|#{e.message}|, " \ |
||||
"this may or may not be expected") |
||||
if fail_on_failed_rpcs |
||||
raise e |
||||
end |
||||
end |
||||
$watchers_mutex.synchronize do |
||||
$watchers.each do |watcher| |
||||
watcher['rpcs_needed'] -= 1 |
||||
if remote_peer.strip.empty? |
||||
watcher['no_remote_peer'] += 1 |
||||
else |
||||
watcher['rpcs_by_peer'][remote_peer] += 1 |
||||
end |
||||
end |
||||
$watchers_cv.broadcast |
||||
end |
||||
end |
||||
end |
||||
|
||||
# Args is used to hold the command line info. |
||||
Args = Struct.new(:fail_on_failed_rpcs, :num_channels, |
||||
:server, :stats_port, :qps) |
||||
|
||||
# validates the command line options, returning them as a Hash. |
||||
def parse_args |
||||
args = Args.new |
||||
args['fail_on_failed_rpcs'] = false |
||||
args['num_channels'] = 1 |
||||
OptionParser.new do |opts| |
||||
opts.on('--fail_on_failed_rpcs BOOL', ['false', 'true']) do |v| |
||||
args['fail_on_failed_rpcs'] = v == 'true' |
||||
end |
||||
opts.on('--num_channels CHANNELS', 'number of channels') do |v| |
||||
args['num_channels'] = v.to_i |
||||
end |
||||
opts.on('--server SERVER_HOST', 'server hostname') do |v| |
||||
GRPC.logger.info("ruby xds: server address is #{v}") |
||||
args['server'] = v |
||||
end |
||||
opts.on('--stats_port STATS_PORT', 'stats port') do |v| |
||||
GRPC.logger.info("ruby xds: stats port is #{v}") |
||||
args['stats_port'] = v |
||||
end |
||||
opts.on('--qps QPS', 'qps') do |v| |
||||
GRPC.logger.info("ruby xds: qps is #{v}") |
||||
args['qps'] = v |
||||
end |
||||
end.parse! |
||||
args |
||||
end |
||||
|
||||
def main |
||||
opts = parse_args |
||||
|
||||
# This server hosts the LoadBalancerStatsService |
||||
host = "0.0.0.0:#{opts['stats_port']}" |
||||
s = GRPC::RpcServer.new |
||||
s.add_http2_port(host, :this_port_is_insecure) |
||||
s.handle(TestTarget) |
||||
server_thread = Thread.new { |
||||
# run the server until the main test runner terminates this process |
||||
s.run_till_terminated_or_interrupted(['TERM']) |
||||
} |
||||
|
||||
# The client just sends unary rpcs continuously in a regular interval |
||||
stub = create_stub(opts) |
||||
target_seconds_between_rpcs = (1.0 / opts['qps'].to_f) |
||||
client_threads = Array.new |
||||
opts['num_channels'].times { |
||||
client_threads << Thread.new { |
||||
run_test_loop(stub, target_seconds_between_rpcs, |
||||
opts['fail_on_failed_rpcs']) |
||||
} |
||||
} |
||||
|
||||
server_thread.join |
||||
$shutdown = true |
||||
client_threads.each { |thd| thd.join } |
||||
end |
||||
|
||||
if __FILE__ == $0 |
||||
main |
||||
end |
@ -0,0 +1,25 @@ |
||||
# Copyright 2020 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
# Config file for the internal CI (in protobuf text format) |
||||
|
||||
# Location of the continuous shell script in repository. |
||||
build_file: "grpc/tools/internal_ci/linux/grpc_xds_csharp.sh" |
||||
timeout_mins: 90 |
||||
action { |
||||
define_artifacts { |
||||
regex: "**/*sponge_log.*" |
||||
regex: "github/grpc/reports/**" |
||||
} |
||||
} |
@ -0,0 +1,26 @@ |
||||
#!/usr/bin/env bash |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
set -ex |
||||
|
||||
# change to grpc repo root |
||||
cd $(dirname $0)/../../.. |
||||
|
||||
source tools/internal_ci/helper_scripts/prepare_build_linux_rc |
||||
|
||||
export DOCKERFILE_DIR=tools/dockerfile/test/csharp_stretch_x64 |
||||
export DOCKER_RUN_SCRIPT=tools/internal_ci/linux/grpc_xds_csharp_test_in_docker.sh |
||||
export OUTPUT_DIR=reports |
||||
exec tools/run_tests/dockerize/build_and_run_docker.sh |
@ -0,0 +1,59 @@ |
||||
#!/usr/bin/env bash |
||||
# Copyright 2020 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
set -ex -o igncr || set -ex |
||||
|
||||
mkdir -p /var/local/git |
||||
git clone /var/local/jenkins/grpc /var/local/git/grpc |
||||
(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \ |
||||
&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \ |
||||
${name}') |
||||
cd /var/local/git/grpc |
||||
|
||||
VIRTUAL_ENV=$(mktemp -d) |
||||
virtualenv "$VIRTUAL_ENV" |
||||
PYTHON="$VIRTUAL_ENV"/bin/python |
||||
"$PYTHON" -m pip install --upgrade pip |
||||
"$PYTHON" -m pip install --upgrade grpcio grpcio-tools google-api-python-client google-auth-httplib2 oauth2client |
||||
|
||||
# Prepare generated Python code. |
||||
TOOLS_DIR=tools/run_tests |
||||
PROTO_SOURCE_DIR=src/proto/grpc/testing |
||||
PROTO_DEST_DIR="$TOOLS_DIR"/"$PROTO_SOURCE_DIR" |
||||
mkdir -p "$PROTO_DEST_DIR" |
||||
touch "$TOOLS_DIR"/src/__init__.py |
||||
touch "$TOOLS_DIR"/src/proto/__init__.py |
||||
touch "$TOOLS_DIR"/src/proto/grpc/__init__.py |
||||
touch "$TOOLS_DIR"/src/proto/grpc/testing/__init__.py |
||||
|
||||
"$PYTHON" -m grpc_tools.protoc \ |
||||
--proto_path=. \ |
||||
--python_out="$TOOLS_DIR" \ |
||||
--grpc_python_out="$TOOLS_DIR" \ |
||||
"$PROTO_SOURCE_DIR"/test.proto \ |
||||
"$PROTO_SOURCE_DIR"/messages.proto \ |
||||
"$PROTO_SOURCE_DIR"/empty.proto |
||||
|
||||
python tools/run_tests/run_tests.py -l csharp -c opt --build_only |
||||
|
||||
GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,cds_lb,eds_lb,priority_lb,weighted_target_lb,lrs_lb "$PYTHON" \ |
||||
tools/run_tests/run_xds_tests.py \ |
||||
--test_case=all \ |
||||
--project_id=grpc-testing \ |
||||
--source_image=projects/grpc-testing/global/images/xds-test-server \ |
||||
--path_to_server_binary=/java_server/grpc-java/interop-testing/build/install/grpc-interop-testing/bin/xds-test-server \ |
||||
--gcp_suffix=$(date '+%s') \ |
||||
--verbose \ |
||||
--client_cmd='dotnet exec src/csharp/Grpc.IntegrationTesting.XdsClient/bin/Release/netcoreapp2.1/Grpc.IntegrationTesting.XdsClient.dll -- --server=xds-experimental:///{server_uri} --stats_port={stats_port} --qps={qps}' |
@ -0,0 +1,25 @@ |
||||
# Copyright 2020 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
# Config file for the internal CI (in protobuf text format) |
||||
|
||||
# Location of the continuous shell script in repository. |
||||
build_file: "grpc/tools/internal_ci/linux/grpc_xds_ruby.sh" |
||||
timeout_mins: 90 |
||||
action { |
||||
define_artifacts { |
||||
regex: "**/*sponge_log.*" |
||||
regex: "github/grpc/reports/**" |
||||
} |
||||
} |
@ -0,0 +1,26 @@ |
||||
#!/usr/bin/env bash |
||||
# Copyright 2017 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
set -ex |
||||
|
||||
# change to grpc repo root |
||||
cd $(dirname $0)/../../.. |
||||
|
||||
source tools/internal_ci/helper_scripts/prepare_build_linux_rc |
||||
|
||||
export DOCKERFILE_DIR=tools/dockerfile/test/ruby_jessie_x64 |
||||
export DOCKER_RUN_SCRIPT=tools/internal_ci/linux/grpc_xds_ruby_test_in_docker.sh |
||||
export OUTPUT_DIR=reports |
||||
exec tools/run_tests/dockerize/build_and_run_docker.sh |
@ -0,0 +1,60 @@ |
||||
#!/usr/bin/env bash |
||||
# Copyright 2020 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
set -ex -o igncr || set -ex |
||||
|
||||
mkdir -p /var/local/git |
||||
git clone /var/local/jenkins/grpc /var/local/git/grpc |
||||
(cd /var/local/jenkins/grpc/ && git submodule foreach 'cd /var/local/git/grpc \ |
||||
&& git submodule update --init --reference /var/local/jenkins/grpc/${name} \ |
||||
${name}') |
||||
cd /var/local/git/grpc |
||||
|
||||
VIRTUAL_ENV=$(mktemp -d) |
||||
virtualenv "$VIRTUAL_ENV" |
||||
PYTHON="$VIRTUAL_ENV"/bin/python |
||||
"$PYTHON" -m pip install --upgrade pip |
||||
"$PYTHON" -m pip install --upgrade grpcio-tools google-api-python-client google-auth-httplib2 oauth2client |
||||
|
||||
# Prepare generated Python code. |
||||
TOOLS_DIR=tools/run_tests |
||||
PROTO_SOURCE_DIR=src/proto/grpc/testing |
||||
PROTO_DEST_DIR="$TOOLS_DIR"/"$PROTO_SOURCE_DIR" |
||||
mkdir -p "$PROTO_DEST_DIR" |
||||
touch "$TOOLS_DIR"/src/__init__.py |
||||
touch "$TOOLS_DIR"/src/proto/__init__.py |
||||
touch "$TOOLS_DIR"/src/proto/grpc/__init__.py |
||||
touch "$TOOLS_DIR"/src/proto/grpc/testing/__init__.py |
||||
|
||||
"$PYTHON" -m grpc_tools.protoc \ |
||||
--proto_path=. \ |
||||
--python_out="$TOOLS_DIR" \ |
||||
--grpc_python_out="$TOOLS_DIR" \ |
||||
"$PROTO_SOURCE_DIR"/test.proto \ |
||||
"$PROTO_SOURCE_DIR"/messages.proto \ |
||||
"$PROTO_SOURCE_DIR"/empty.proto |
||||
|
||||
(cd src/ruby && bundle && rake compile) |
||||
|
||||
GRPC_VERBOSITY=debug GRPC_TRACE=xds_client,xds_resolver,cds_lb,eds_lb,priority_lb,weighted_target_lb,lrs_lb "$PYTHON" \ |
||||
tools/run_tests/run_xds_tests.py \ |
||||
--test_case=all \ |
||||
--project_id=grpc-testing \ |
||||
--source_image=projects/grpc-testing/global/images/xds-test-server \ |
||||
--path_to_server_binary=/java_server/grpc-java/interop-testing/build/install/grpc-interop-testing/bin/xds-test-server \ |
||||
--gcp_suffix=$(date '+%s') \ |
||||
--only_stable_gcp_apis \ |
||||
--verbose \ |
||||
--client_cmd='ruby src/ruby/pb/test/xds_client.rb --server=xds-experimental:///{server_uri} --stats_port={stats_port} --qps={qps}' |
Loading…
Reference in new issue