1#!/usr/bin/env ruby 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 17this_dir = File.expand_path(File.dirname(__FILE__)) 18protos_lib_dir = File.join(this_dir, 'lib') 19grpc_lib_dir = File.join(File.dirname(this_dir), 'lib') 20$LOAD_PATH.unshift(grpc_lib_dir) unless $LOAD_PATH.include?(grpc_lib_dir) 21$LOAD_PATH.unshift(protos_lib_dir) unless $LOAD_PATH.include?(protos_lib_dir) 22$LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir) 23 24require 'grpc' 25require 'echo_services_pb' 26require 'client_control_services_pb' 27require 'socket' 28require 'optparse' 29require 'thread' 30require 'timeout' 31require 'English' # see https://github.com/bbatsov/rubocop/issues/1747 32require_relative '../spec/support/helpers' 33 34include GRPC::Spec::Helpers 35 36# Useful to update a value within a do block 37class MutableValue 38 attr_accessor :value 39 40 def initialize(value) 41 @value = value 42 end 43end 44 45class EchoServerImpl < Echo::EchoServer::Service 46 def echo(echo_req, _) 47 Echo::EchoReply.new(response: echo_req.request) 48 end 49end 50 51class SecureEchoServerImpl < Echo::EchoServer::Service 52 def echo(echo_req, call) 53 unless call.metadata["authorization"] == 'test' 54 fail "expected authorization header with value: test" 55 end 56 Echo::EchoReply.new(response: echo_req.request) 57 end 58end 59 60# ServerRunner starts an "echo server" that test clients can make calls to 61class ServerRunner 62 attr_accessor :server_creds 63 64 def initialize(service_impl, rpc_server_args: {}) 65 @service_impl = service_impl 66 @rpc_server_args = rpc_server_args 67 @server_creds = :this_port_is_insecure 68 end 69 70 def run 71 @srv = new_rpc_server_for_testing(@rpc_server_args) 72 port = @srv.add_http2_port('0.0.0.0:0', @server_creds) 73 @srv.handle(@service_impl) 74 75 @thd = Thread.new do 76 @srv.run 77 end 78 @srv.wait_till_running 79 port 80 end 81 82 def stop 83 @srv.stop 84 @thd.join 85 fail 'server not stopped' unless @srv.stopped? 86 end 87end 88 89# ClientController is used to start a child process and communicate 90# with it for test orchestration purposes via RPCs. 91class ClientController < ClientControl::ParentController::Service 92 attr_reader :stub, :client_pid 93 94 def initialize(client_main, server_port) 95 this_dir = File.expand_path(File.dirname(__FILE__)) 96 client_path = File.join(this_dir, client_main) 97 @server = new_rpc_server_for_testing(poll_period: 3) 98 port = @server.add_http2_port('localhost:0', :this_port_is_insecure) 99 server_thread = Thread.new do 100 @server.handle(self) 101 @server.run 102 end 103 @server.wait_till_running 104 @client_controller_port_mu = Mutex.new 105 @client_controller_port_cv = ConditionVariable.new 106 @client_controller_port = nil 107 @client_pid = Process.spawn(RbConfig.ruby, 108 client_path, 109 "--parent_controller_port=#{port}", 110 "--server_port=#{server_port}") 111 begin 112 Timeout.timeout(60) do 113 @client_controller_port_mu.synchronize do 114 while @client_controller_port.nil? 115 @client_controller_port_cv.wait(@client_controller_port_mu) 116 end 117 end 118 end 119 rescue => e 120 fail "timeout waiting for child process to report port. error: #{e}" 121 end 122 @server.stop 123 server_thread.join 124 @stub = ClientControl::ClientController::Stub.new( 125 "localhost:#{@client_controller_port}", :this_channel_is_insecure) 126 end 127 128 def set_client_controller_port(req, _) 129 @client_controller_port_mu.synchronize do 130 unless @client_controller_port.nil? 131 fail 'client controller port already set' 132 end 133 @client_controller_port = req.port 134 @client_controller_port_cv.broadcast 135 end 136 ClientControl::Void.new 137 end 138end 139 140def report_controller_port_to_parent(parent_controller_port, client_controller_port) 141 unless parent_controller_port.to_i > 0 142 fail "bad parent control port: |#{parent_controller_port}|" 143 end 144 stub = ClientControl::ParentController::Stub.new( 145 "localhost:#{parent_controller_port.to_i}", :this_channel_is_insecure) 146 m = ClientControl::Port.new 147 m.port = client_controller_port.to_i 148 stub.set_client_controller_port(m, deadline: Time.now + 10) 149end 150 151def with_logging(action) 152 STDERR.puts "#{action}: begin (pid=#{Process.pid})" 153 yield 154 STDERR.puts "#{action}: done (pid=#{Process.pid})" 155end 156