Merge github.com:google/grpc into async-api

pull/357/head
Craig Tiller 10 years ago
commit d74fc6571c
  1. 2
      src/core/iomgr/pollset_kick.c
  2. 4
      src/core/iomgr/pollset_posix.h
  3. 7
      src/cpp/server/thread_pool.cc
  4. 10
      src/ruby/Rakefile
  5. 44
      src/ruby/bin/apis/google/protobuf/empty.rb
  6. 278
      src/ruby/bin/apis/pubsub_demo.rb
  7. 174
      src/ruby/bin/apis/tech/pubsub/proto/pubsub.rb
  8. 103
      src/ruby/bin/apis/tech/pubsub/proto/pubsub_services.rb
  9. 178
      src/ruby/bin/interop/interop_client.rb
  10. 5
      src/ruby/bin/interop/test/cpp/interop/messages.rb
  11. 0
      src/ruby/bin/math.rb
  12. 0
      src/ruby/bin/math_services.rb
  13. 10
      src/ruby/grpc.gemspec
  14. 2
      src/ruby/lib/grpc.rb
  15. 69
      src/ruby/lib/grpc/auth/compute_engine.rb
  16. 68
      src/ruby/lib/grpc/auth/service_account.rb
  17. 67
      src/ruby/lib/grpc/auth/signet.rb
  18. 163
      src/ruby/spec/auth/apply_auth_examples.rb
  19. 108
      src/ruby/spec/auth/compute_engine_spec.rb
  20. 75
      src/ruby/spec/auth/service_account_spec.rb
  21. 70
      src/ruby/spec/auth/signet_spec.rb
  22. 4
      src/ruby/spec/channel_spec.rb
  23. 2
      src/ruby/spec/generic/active_call_spec.rb
  24. 12
      src/ruby/spec/spec_helper.rb
  25. 4
      tools/gce_setup/interop_test_runner.sh

@ -142,11 +142,13 @@ void grpc_pollset_kick_global_init_fallback_fd(void) {
}
void grpc_pollset_kick_global_init(void) {
gpr_mu_init(&fd_freelist_mu);
grpc_wakeup_fd_global_init();
}
void grpc_pollset_kick_global_destroy(void) {
grpc_wakeup_fd_global_destroy();
gpr_mu_destroy(&fd_freelist_mu);
}

@ -79,7 +79,9 @@ void grpc_pollset_add_fd(grpc_pollset *pollset, struct grpc_fd *fd);
void grpc_pollset_del_fd(grpc_pollset *pollset, struct grpc_fd *fd);
/* Force any current pollers to break polling: it's the callers responsibility
to ensure that the pollset indeed needs to be kicked.
to ensure that the pollset indeed needs to be kicked - no verification that
the pollset is actually performing polling work is done. At worst this will
result in spurious wakeups if performed at the wrong moment.
Does not touch pollset->mu. */
void grpc_pollset_force_kick(grpc_pollset *pollset);
/* Returns the fd to listen on for kicks */

@ -41,7 +41,10 @@ ThreadPool::ThreadPool(int num_threads) {
for (;;) {
std::unique_lock<std::mutex> lock(mu_);
// Wait until work is available or we are shutting down.
cv_.wait(lock, [=]() { return shutdown_ || !callbacks_.empty(); });
auto have_work = [=]() { return shutdown_ || !callbacks_.empty(); };
if (!have_work()) {
cv_.wait(lock, have_work);
}
// Drain callbacks before considering shutdown to ensure all work
// gets completed.
if (!callbacks_.empty()) {
@ -71,7 +74,7 @@ ThreadPool::~ThreadPool() {
void ThreadPool::ScheduleCallback(const std::function<void()> &callback) {
std::lock_guard<std::mutex> lock(mu_);
callbacks_.push(callback);
cv_.notify_all();
cv_.notify_one();
}
} // namespace grpc

@ -35,18 +35,20 @@ namespace :spec do
t.pattern = spec_files
t.rspec_opts = "--tag #{suite[:tag]}" if suite[:tag]
t.rspec_opts = suite[:tags].map{ |t| "--tag #{t}" }.join(' ') if suite[:tags]
if suite[:tags]
t.rspec_opts = suite[:tags].map { |x| "--tag #{x}" }.join(' ')
end
end
end
end
end
desc 'Run compiles the extension, runs all the tests'
desc 'Compiles the extension then runs all the tests'
task :all
task default: :all
task 'spec:suite:wrapper' => :compile
task 'spec:suite:wrapper' => [:compile, :rubocop]
task 'spec:suite:idiomatic' => 'spec:suite:wrapper'
task 'spec:suite:bidi' => 'spec:suite:wrapper'
task 'spec:suite:server' => 'spec:suite:wrapper'
task :all => ['spec:suite:idiomatic', 'spec:suite:bidi', 'spec:suite:server']
task all: ['spec:suite:idiomatic', 'spec:suite:bidi', 'spec:suite:server']

@ -0,0 +1,44 @@
# Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/protobuf/empty.proto
require 'google/protobuf'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_message "google.protobuf.Empty" do
end
end
module Google
module Protobuf
Empty = Google::Protobuf::DescriptorPool.generated_pool.lookup("google.protobuf.Empty").msgclass
end
end

@ -0,0 +1,278 @@
#!/usr/bin/env ruby
# Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# pubsub_demo demos accesses the Google PubSub API via its gRPC interface
#
# TODO: update the Usage once the usable auth gem is available
# $ SSL_CERT_FILE=<path/to/ssl/certs> \
# path/to/pubsub_demo.rb \
# --service_account_key_file=<path_to_service_account> \
# [--action=<chosen_demo_action> ]
#
# There are options related to the chosen action, see #parse_args below.
# - the possible actions are given by the method names of NamedAction class
# - the default action is list_some_topics
this_dir = File.expand_path(File.dirname(__FILE__))
lib_dir = File.join(File.dirname(File.dirname(this_dir)), 'lib')
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
$LOAD_PATH.unshift(this_dir) unless $LOAD_PATH.include?(this_dir)
require 'optparse'
require 'grpc'
require 'google/protobuf'
require 'google/protobuf/empty'
require 'tech/pubsub/proto/pubsub'
require 'tech/pubsub/proto/pubsub_services'
# loads the certificates used to access the test server securely.
def load_prod_cert
fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil?
p "loading prod certs from #{ENV['SSL_CERT_FILE']}"
File.open(ENV['SSL_CERT_FILE']).read
end
# creates a SSL Credentials from the production certificates.
def ssl_creds
GRPC::Core::Credentials.new(load_prod_cert)
end
# Builds the metadata authentication update proc.
#
# TODO: replace this once the ruby usable auth repo is available.
def auth_proc(opts)
if GRPC::Auth::GCECredentials.on_gce?
return GRPC::Auth::GCECredentials.new.updater_proc
end
fd = StringIO.new(File.read(opts.oauth_key_file))
GRPC::Auth::ServiceAccountCredentials.new(opts.oauth_scope, fd).updater_proc
end
# Creates a stub for accessing the publisher service.
def publisher_stub(opts)
address = "#{opts.host}:#{opts.port}"
stub_clz = Tech::Pubsub::PublisherService::Stub # shorter
logger.info("... access PublisherService at #{address}")
stub_clz.new(address,
creds: ssl_creds, update_metadata: auth_proc(opts),
GRPC::Core::Channel::SSL_TARGET => opts.host)
end
# Creates a stub for accessing the subscriber service.
def subscriber_stub(opts)
address = "#{opts.host}:#{opts.port}"
stub_clz = Tech::Pubsub::SubscriberService::Stub # shorter
logger.info("... access SubscriberService at #{address}")
stub_clz.new(address,
creds: ssl_creds, update_metadata: auth_proc(opts),
GRPC::Core::Channel::SSL_TARGET => opts.host)
end
# defines methods corresponding to each interop test case.
class NamedActions
include Tech::Pubsub
# Initializes NamedActions
#
# @param pub [Stub] a stub for accessing the publisher service
# @param sub [Stub] a stub for accessing the publisher service
# @param args [Args] provides access to the command line
def initialize(pub, sub, args)
@pub = pub
@sub = sub
@args = args
end
# Removes the test topic if it exists
def remove_topic
name = test_topic_name
p "... removing Topic #{name}"
@pub.delete_topic(DeleteTopicRequest.new(topic: name))
p "removed Topic: #{name} OK"
rescue GRPC::BadStatus => e
p "Could not delete a topics: rpc failed with '#{e}'"
end
# Creates a test topic
def create_topic
name = test_topic_name
p "... creating Topic #{name}"
resp = @pub.create_topic(Topic.new(name: name))
p "created Topic: #{resp.name} OK"
rescue GRPC::BadStatus => e
p "Could not create a topics: rpc failed with '#{e}'"
end
# Lists topics in the project
def list_some_topics
p 'Listing topics'
p '-------------_'
list_project_topics.topic.each { |t| p t.name }
rescue GRPC::BadStatus => e
p "Could not list topics: rpc failed with '#{e}'"
end
# Checks if a topics exists in a project
def check_exists
name = test_topic_name
p "... checking for topic #{name}"
exists = topic_exists?(name)
p "#{name} is a topic" if exists
p "#{name} is not a topic" unless exists
rescue GRPC::BadStatus => e
p "Could not check for a topics: rpc failed with '#{e}'"
end
# Publishes some messages
def random_pub_sub
topic_name, sub_name = test_topic_name, test_sub_name
create_topic_if_needed(topic_name)
@sub.create_subscription(Subscription.new(name: sub_name,
topic: topic_name))
msg_count = rand(10..30)
msg_count.times do |x|
msg = PubsubMessage.new(data: "message #{x}")
@pub.publish(PublishRequest.new(topic: topic_name, message: msg))
end
p "Sent #{msg_count} messages to #{topic_name}, checking for them now."
batch = @sub.pull_batch(PullBatchRequest.new(subscription: sub_name,
max_events: msg_count))
ack_ids = batch.pull_responses.map { |x| x.ack_id }
p "Got #{ack_ids.size} messages; acknowledging them.."
@sub.acknowledge(AcknowledgeRequest.new(subscription: sub_name,
ack_id: ack_ids))
p "Test messages were acknowledged OK, deleting the subscription"
del_req = DeleteSubscriptionRequest.new(subscription: sub_name)
@sub.delete_subscription(del_req)
rescue GRPC::BadStatus => e
p "Could not do random pub sub: rpc failed with '#{e}'"
end
private
# test_topic_name is the topic name to use in this test.
def test_topic_name
unless @args.topic_name.nil?
return "/topics/#{@args.project_id}/#{@args.topic_name}"
end
now_text = Time.now.utc.strftime('%Y%m%d%H%M%S%L')
"/topics/#{@args.project_id}/#{ENV['USER']}-#{now_text}"
end
# test_sub_name is the subscription name to use in this test.
def test_sub_name
unless @args.sub_name.nil?
return "/subscriptions/#{@args.project_id}/#{@args.sub_name}"
end
now_text = Time.now.utc.strftime('%Y%m%d%H%M%S%L')
"/subscriptions/#{@args.project_id}/#{ENV['USER']}-#{now_text}"
end
# determines if the topic name exists
def topic_exists?(name)
topics = list_project_topics.topic.map { |t| t.name }
topics.include?(name)
end
def create_topic_if_needed(name)
return if topic_exists?(name)
@pub.create_topic(Topic.new(name: name))
end
def list_project_topics
q = "cloud.googleapis.com/project in (/projects/#{@args.project_id})"
@pub.list_topics(ListTopicsRequest.new(query: q))
end
end
# Args is used to hold the command line info.
Args = Struct.new(:host, :oauth_scope, :oauth_key_file, :port, :action,
:project_id, :topic_name, :sub_name)
# validates the the command line options, returning them as an Arg.
def parse_args
args = Args.new('pubsub-staging.googleapis.com',
'https://www.googleapis.com/auth/pubsub',
nil, 443, 'list_some_topics', 'stoked-keyword-656')
OptionParser.new do |opts|
opts.on('--oauth_scope scope',
'Scope for OAuth tokens') { |v| args['oauth_scope'] = v }
opts.on('--server_host SERVER_HOST', 'server hostname') do |v|
args.host = v
end
opts.on('--server_port SERVER_PORT', 'server port') do |v|
args.port = v
end
opts.on('--service_account_key_file PATH',
'Path to the service account json key file') do |v|
args.oauth_key_file = v
end
# instance_methods(false) gives only the methods defined in that class.
scenes = NamedActions.instance_methods(false).map { |t| t.to_s }
scene_list = scenes.join(',')
opts.on("--action CODE", scenes, {}, 'pick a demo action',
" (#{scene_list})") do |v|
args.action = v
end
# Set the remaining values.
%w(project_id topic_name sub_name).each do |o|
opts.on("--#{o} VALUE", "#{o}") do |v|
args[o] = v
end
end
end.parse!
_check_args(args)
end
def _check_args(args)
%w(host port action).each do |a|
if args[a].nil?
raise OptionParser::MissingArgument.new("please specify --#{a}")
end
end
if args['oauth_key_file'].nil? || args['oauth_scope'].nil?
fail(OptionParser::MissingArgument,
'please specify both of --service_account_key_file and --oauth_scope')
end
args
end
def main
args = parse_args
pub, sub = publisher_stub(args), subscriber_stub(args)
NamedActions.new(pub, sub, args).method(args.action).call
end
main

@ -0,0 +1,174 @@
# Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: tech/pubsub/proto/pubsub.proto
require 'google/protobuf'
require 'google/protobuf/empty'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_message "tech.pubsub.Topic" do
optional :name, :string, 1
end
add_message "tech.pubsub.PubsubMessage" do
optional :data, :string, 1
optional :message_id, :string, 3
end
add_message "tech.pubsub.GetTopicRequest" do
optional :topic, :string, 1
end
add_message "tech.pubsub.PublishRequest" do
optional :topic, :string, 1
optional :message, :message, 2, "tech.pubsub.PubsubMessage"
end
add_message "tech.pubsub.PublishBatchRequest" do
optional :topic, :string, 1
repeated :messages, :message, 2, "tech.pubsub.PubsubMessage"
end
add_message "tech.pubsub.PublishBatchResponse" do
repeated :message_ids, :string, 1
end
add_message "tech.pubsub.ListTopicsRequest" do
optional :query, :string, 1
optional :max_results, :int32, 2
optional :page_token, :string, 3
end
add_message "tech.pubsub.ListTopicsResponse" do
repeated :topic, :message, 1, "tech.pubsub.Topic"
optional :next_page_token, :string, 2
end
add_message "tech.pubsub.DeleteTopicRequest" do
optional :topic, :string, 1
end
add_message "tech.pubsub.Subscription" do
optional :name, :string, 1
optional :topic, :string, 2
optional :query, :string, 3
optional :truncation_policy, :message, 4, "tech.pubsub.Subscription.TruncationPolicy"
optional :push_config, :message, 5, "tech.pubsub.PushConfig"
optional :ack_deadline_seconds, :int32, 6
optional :garbage_collect_seconds, :int64, 7
end
add_message "tech.pubsub.Subscription.TruncationPolicy" do
optional :max_bytes, :int64, 1
optional :max_age_seconds, :int64, 2
end
add_message "tech.pubsub.PushConfig" do
optional :push_endpoint, :string, 1
end
add_message "tech.pubsub.PubsubEvent" do
optional :subscription, :string, 1
optional :message, :message, 2, "tech.pubsub.PubsubMessage"
optional :truncated, :bool, 3
optional :deleted, :bool, 4
end
add_message "tech.pubsub.GetSubscriptionRequest" do
optional :subscription, :string, 1
end
add_message "tech.pubsub.ListSubscriptionsRequest" do
optional :query, :string, 1
optional :max_results, :int32, 3
optional :page_token, :string, 4
end
add_message "tech.pubsub.ListSubscriptionsResponse" do
repeated :subscription, :message, 1, "tech.pubsub.Subscription"
optional :next_page_token, :string, 2
end
add_message "tech.pubsub.TruncateSubscriptionRequest" do
optional :subscription, :string, 1
end
add_message "tech.pubsub.DeleteSubscriptionRequest" do
optional :subscription, :string, 1
end
add_message "tech.pubsub.ModifyPushConfigRequest" do
optional :subscription, :string, 1
optional :push_config, :message, 2, "tech.pubsub.PushConfig"
end
add_message "tech.pubsub.PullRequest" do
optional :subscription, :string, 1
optional :return_immediately, :bool, 2
end
add_message "tech.pubsub.PullResponse" do
optional :ack_id, :string, 1
optional :pubsub_event, :message, 2, "tech.pubsub.PubsubEvent"
end
add_message "tech.pubsub.PullBatchRequest" do
optional :subscription, :string, 1
optional :return_immediately, :bool, 2
optional :max_events, :int32, 3
end
add_message "tech.pubsub.PullBatchResponse" do
repeated :pull_responses, :message, 2, "tech.pubsub.PullResponse"
end
add_message "tech.pubsub.ModifyAckDeadlineRequest" do
optional :subscription, :string, 1
optional :ack_id, :string, 2
optional :ack_deadline_seconds, :int32, 3
end
add_message "tech.pubsub.AcknowledgeRequest" do
optional :subscription, :string, 1
repeated :ack_id, :string, 2
end
add_message "tech.pubsub.NackRequest" do
optional :subscription, :string, 1
repeated :ack_id, :string, 2
end
end
module Tech
module Pubsub
Topic = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.Topic").msgclass
PubsubMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PubsubMessage").msgclass
GetTopicRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.GetTopicRequest").msgclass
PublishRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PublishRequest").msgclass
PublishBatchRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PublishBatchRequest").msgclass
PublishBatchResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PublishBatchResponse").msgclass
ListTopicsRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListTopicsRequest").msgclass
ListTopicsResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListTopicsResponse").msgclass
DeleteTopicRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.DeleteTopicRequest").msgclass
Subscription = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.Subscription").msgclass
Subscription::TruncationPolicy = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.Subscription.TruncationPolicy").msgclass
PushConfig = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PushConfig").msgclass
PubsubEvent = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PubsubEvent").msgclass
GetSubscriptionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.GetSubscriptionRequest").msgclass
ListSubscriptionsRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListSubscriptionsRequest").msgclass
ListSubscriptionsResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ListSubscriptionsResponse").msgclass
TruncateSubscriptionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.TruncateSubscriptionRequest").msgclass
DeleteSubscriptionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.DeleteSubscriptionRequest").msgclass
ModifyPushConfigRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ModifyPushConfigRequest").msgclass
PullRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullRequest").msgclass
PullResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullResponse").msgclass
PullBatchRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullBatchRequest").msgclass
PullBatchResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.PullBatchResponse").msgclass
ModifyAckDeadlineRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.ModifyAckDeadlineRequest").msgclass
AcknowledgeRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.AcknowledgeRequest").msgclass
NackRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("tech.pubsub.NackRequest").msgclass
end
end

@ -0,0 +1,103 @@
# Copyright 2014, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# Source: tech/pubsub/proto/pubsub.proto for package 'tech.pubsub'
require 'grpc'
require 'google/protobuf/empty'
require 'tech/pubsub/proto/pubsub'
module Tech
module Pubsub
module PublisherService
# TODO: add proto service documentation here
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'tech.pubsub.PublisherService'
rpc :CreateTopic, Topic, Topic
rpc :Publish, PublishRequest, Google::Protobuf::Empty
rpc :PublishBatch, PublishBatchRequest, PublishBatchResponse
rpc :GetTopic, GetTopicRequest, Topic
rpc :ListTopics, ListTopicsRequest, ListTopicsResponse
rpc :DeleteTopic, DeleteTopicRequest, Google::Protobuf::Empty
end
Stub = Service.rpc_stub_class
end
module SubscriberService
# TODO: add proto service documentation here
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'tech.pubsub.SubscriberService'
rpc :CreateSubscription, Subscription, Subscription
rpc :GetSubscription, GetSubscriptionRequest, Subscription
rpc :ListSubscriptions, ListSubscriptionsRequest, ListSubscriptionsResponse
rpc :DeleteSubscription, DeleteSubscriptionRequest, Google::Protobuf::Empty
rpc :TruncateSubscription, TruncateSubscriptionRequest, Google::Protobuf::Empty
rpc :ModifyPushConfig, ModifyPushConfigRequest, Google::Protobuf::Empty
rpc :Pull, PullRequest, PullResponse
rpc :PullBatch, PullBatchRequest, PullBatchResponse
rpc :ModifyAckDeadline, ModifyAckDeadlineRequest, Google::Protobuf::Empty
rpc :Acknowledge, AcknowledgeRequest, Google::Protobuf::Empty
rpc :Nack, NackRequest, Google::Protobuf::Empty
end
Stub = Service.rpc_stub_class
end
module PushEndpointService
# TODO: add proto service documentation here
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'tech.pubsub.PushEndpointService'
rpc :HandlePubsubEvent, PubsubEvent, Google::Protobuf::Empty
end
Stub = Service.rpc_stub_class
end
end
end

@ -56,6 +56,8 @@ require 'test/cpp/interop/empty'
require 'signet/ssl_config'
include Google::RPC::Auth
# loads the certificates used to access the test server securely.
def load_test_certs
this_dir = File.expand_path(File.dirname(__FILE__))
@ -67,40 +69,54 @@ end
# loads the certificates used to access the test server securely.
def load_prod_cert
fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil?
p "loading prod certs from #{ENV['SSL_CERT_FILE']}"
logger.info("loading prod certs from #{ENV['SSL_CERT_FILE']}")
File.open(ENV['SSL_CERT_FILE']).read
end
# creates a Credentials from the test certificates.
# creates SSL Credentials from the test certificates.
def test_creds
certs = load_test_certs
GRPC::Core::Credentials.new(certs[0])
end
RX_CERT = /-----BEGIN CERTIFICATE-----\n.*?-----END CERTIFICATE-----\n/m
# creates a Credentials from the production certificates.
# creates SSL Credentials from the production certificates.
def prod_creds
cert_text = load_prod_cert
GRPC::Core::Credentials.new(cert_text)
end
# creates a test stub that accesses host:port securely.
def create_stub(host, port, is_secure, host_override, use_test_ca)
address = "#{host}:#{port}"
if is_secure
creds = nil
if use_test_ca
creds = test_creds
else
creds = prod_creds
# creates the SSL Credentials.
def ssl_creds(use_test_ca)
return test_creds if use_test_ca
prod_creds
end
# creates a test stub that accesses host:port securely.
def create_stub(opts)
address = "#{opts.host}:#{opts.port}"
if opts.secure
stub_opts = {
:creds => creds,
GRPC::Core::Channel::SSL_TARGET => host_override
:creds => ssl_creds(opts.use_test_ca),
GRPC::Core::Channel::SSL_TARGET => opts.host_override
}
# Add service account creds if specified
if %w(all service_account_creds).include?(opts.test_case)
unless opts.oauth_scope.nil?
fd = StringIO.new(File.read(opts.oauth_key_file))
logger.info("loading oauth certs from #{opts.oauth_key_file}")
auth_creds = ServiceAccountCredentials.new(opts.oauth_scope, fd)
stub_opts[:update_metadata] = auth_creds.updater_proc
end
end
# Add compute engine creds if specified
if %w(all compute_engine_creds).include?(opts.test_case)
unless opts.oauth_scope.nil?
stub_opts[:update_metadata] = GCECredentials.new.update_proc
end
end
logger.info("... connecting securely to #{address}")
Grpc::Testing::TestService::Stub.new(address, **stub_opts)
else
@ -158,9 +174,10 @@ class NamedTests
include Grpc::Testing::PayloadType
attr_accessor :assertions # required by Minitest::Assertions
def initialize(stub)
def initialize(stub, args)
@assertions = 0 # required by Minitest::Assertions
@stub = stub
@args = args
end
def empty_unary
@ -170,21 +187,37 @@ class NamedTests
end
def large_unary
req_size, wanted_response_size = 271_828, 314_159
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
req = SimpleRequest.new(response_type: :COMPRESSABLE,
response_size: wanted_response_size,
payload: payload)
resp = @stub.unary_call(req)
assert_equal(:COMPRESSABLE, resp.payload.type,
'large_unary: payload had the wrong type')
assert_equal(wanted_response_size, resp.payload.body.length,
'large_unary: payload had the wrong length')
assert_equal(nulls(wanted_response_size), resp.payload.body,
'large_unary: payload content is invalid')
perform_large_unary
p 'OK: large_unary'
end
def service_account_creds
# ignore this test if the oauth options are not set
if @args.oauth_scope.nil? || @args.oauth_key_file.nil?
p 'NOT RUN: service_account_creds; no service_account settings'
return
end
json_key = File.read(@args.oauth_key_file)
wanted_email = MultiJson.load(json_key)['client_email']
resp = perform_large_unary(fill_username: true,
fill_oauth_scope: true)
assert_equal(wanted_email, resp.username,
'service_account_creds: incorrect username')
assert(@args.oauth_scope.include?(resp.oauth_scope),
'service_account_creds: incorrect oauth_scope')
p 'OK: service_account_creds'
end
def compute_engine_creds
resp = perform_large_unary(fill_username: true,
fill_oauth_scope: true)
assert(@args.oauth_scope.include?(resp.oauth_scope),
'service_account_creds: incorrect oauth_scope')
assert_equal(@args.default_service_account, resp.username,
'service_account_creds: incorrect username')
p 'OK: compute_engine_creds'
end
def client_streaming
msg_sizes = [27_182, 8, 1828, 45_904]
wanted_aggregate_size = 74_922
@ -230,64 +263,89 @@ class NamedTests
method(m).call
end
end
private
def perform_large_unary(fill_username: false, fill_oauth_scope: false)
req_size, wanted_response_size = 271_828, 314_159
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
req = SimpleRequest.new(response_type: :COMPRESSABLE,
response_size: wanted_response_size,
payload: payload)
req.fill_username = fill_username
req.fill_oauth_scope = fill_oauth_scope
resp = @stub.unary_call(req)
assert_equal(:COMPRESSABLE, resp.payload.type,
'large_unary: payload had the wrong type')
assert_equal(wanted_response_size, resp.payload.body.length,
'large_unary: payload had the wrong length')
assert_equal(nulls(wanted_response_size), resp.payload.body,
'large_unary: payload content is invalid')
resp
end
end
# Args is used to hold the command line info.
Args = Struct.new(:default_service_account, :host, :host_override,
:oauth_scope, :oauth_key_file, :port, :secure, :test_case,
:use_test_ca)
# validates the the command line options, returning them as a Hash.
def parse_options
options = {
'secure' => false,
'server_host' => nil,
'server_host_override' => nil,
'server_port' => nil,
'test_case' => nil
}
def parse_args
args = Args.new
args.host_override = 'foo.test.google.com'
OptionParser.new do |opts|
opts.banner = 'Usage: --server_host <server_host> --server_port server_port'
opts.on('--oauth_scope scope',
'Scope for OAuth tokens') { |v| args['oauth_scope'] = v }
opts.on('--server_host SERVER_HOST', 'server hostname') do |v|
options['server_host'] = v
args['host'] = v
end
opts.on('--default_service_account email_address',
'email address of the default service account') do |v|
args['default_service_account'] = v
end
opts.on('--service_account_key_file PATH',
'Path to the service account json key file') do |v|
args['oauth_key_file'] = v
end
opts.on('--server_host_override HOST_OVERRIDE',
'override host via a HTTP header') do |v|
options['server_host_override'] = v
end
opts.on('--server_port SERVER_PORT', 'server port') do |v|
options['server_port'] = v
args['host_override'] = v
end
opts.on('--server_port SERVER_PORT', 'server port') { |v| args['port'] = v }
# instance_methods(false) gives only the methods defined in that class
test_cases = NamedTests.instance_methods(false).map(&:to_s)
test_case_list = test_cases.join(',')
opts.on('--test_case CODE', test_cases, {}, 'select a test_case',
" (#{test_case_list})") do |v|
options['test_case'] = v
end
" (#{test_case_list})") { |v| args['test_case'] = v }
opts.on('-s', '--use_tls', 'require a secure connection?') do |v|
options['secure'] = v
args['secure'] = v
end
opts.on('-t', '--use_test_ca',
'if secure, use the test certificate?') do |v|
options['use_test_ca'] = v
args['use_test_ca'] = v
end
end.parse!
_check_options(options)
_check_args(args)
end
def _check_options(opts)
%w(server_host server_port test_case).each do |arg|
if opts[arg].nil?
def _check_args(args)
%w(host port test_case).each do |a|
if args[a].nil?
fail(OptionParser::MissingArgument, "please specify --#{arg}")
end
end
if opts['server_host_override'].nil?
opts['server_host_override'] = opts['server_host']
if args['oauth_key_file'].nil? ^ args['oauth_scope'].nil?
fail(OptionParser::MissingArgument,
'please specify both of --service_account_key_file and --oauth_scope')
end
opts
args
end
def main
opts = parse_options
stub = create_stub(opts['server_host'], opts['server_port'], opts['secure'],
opts['server_host_override'], opts['use_test_ca'])
NamedTests.new(stub).method(opts['test_case']).call
opts = parse_args
stub = create_stub(opts)
NamedTests.new(stub, opts).method(opts['test_case']).call
end
main

@ -41,10 +41,13 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
optional :response_type, :enum, 1, "grpc.testing.PayloadType"
optional :response_size, :int32, 2
optional :payload, :message, 3, "grpc.testing.Payload"
optional :fill_username, :bool, 4
optional :fill_oauth_scope, :bool, 5
end
add_message "grpc.testing.SimpleResponse" do
optional :payload, :message, 1, "grpc.testing.Payload"
optional :effective_gaia_user_id, :int64, 2
optional :username, :string, 2
optional :oauth_scope, :string, 3
end
add_message "grpc.testing.StreamingInputCallRequest" do
optional :payload, :message, 1, "grpc.testing.Payload"

@ -1,3 +1,4 @@
# -*- ruby -*-
# encoding: utf-8
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
require 'grpc/version'
@ -19,11 +20,14 @@ Gem::Specification.new do |s|
s.require_paths = ['lib']
s.platform = Gem::Platform::RUBY
s.add_dependency 'xray'
s.add_dependency 'logging', '~> 1.8'
s.add_dependency 'faraday', '~> 0.9'
s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
s.add_dependency 'signet', '~> 0.5.1'
s.add_dependency 'logging', '~> 1.8'
s.add_dependency 'jwt', '~> 1.2.1'
s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests
s.add_dependency 'multijson', '1.10.1'
s.add_dependency 'signet', '~> 0.6.0'
s.add_dependency 'xray', '~> 1.1'
s.add_development_dependency 'bundler', '~> 1.7'
s.add_development_dependency 'rake', '~> 10.0'

@ -27,6 +27,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc/auth/compute_engine.rb'
require 'grpc/auth/service_account.rb'
require 'grpc/errors'
require 'grpc/grpc'
require 'grpc/logconfig'

@ -0,0 +1,69 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'faraday'
require 'grpc/auth/signet'
module Google
module RPC
# Module Auth provides classes that provide Google-specific authentication
# used to access Google gRPC services.
module Auth
# Extends Signet::OAuth2::Client so that the auth token is obtained from
# the GCE metadata server.
class GCECredentials < Signet::OAuth2::Client
COMPUTE_AUTH_TOKEN_URI = 'http://metadata/computeMetadata/v1/'\
'instance/service-accounts/default/token'
COMPUTE_CHECK_URI = 'http://metadata.google.internal'
# Detect if this appear to be a GCE instance, by checking if metadata
# is available
def self.on_gce?(options = {})
c = options[:connection] || Faraday.default_connection
resp = c.get(COMPUTE_CHECK_URI)
return false unless resp.status == 200
return false unless resp.headers.key?('Metadata-Flavor')
return resp.headers['Metadata-Flavor'] == 'Google'
rescue Faraday::ConnectionFailed
return false
end
# Overrides the super class method to change how access tokens are
# fetched.
def fetch_access_token(options = {})
c = options[:connection] || Faraday.default_connection
c.headers = { 'Metadata-Flavor' => 'Google' }
resp = c.get(COMPUTE_AUTH_TOKEN_URI)
Signet::OAuth2.parse_credentials(resp.body,
resp.headers['content-type'])
end
end
end
end
end

@ -0,0 +1,68 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'grpc/auth/signet'
require 'multi_json'
require 'openssl'
# Reads the private key and client email fields from service account JSON key.
def read_json_key(json_key_io)
json_key = MultiJson.load(json_key_io.read)
fail 'missing client_email' unless json_key.key?('client_email')
fail 'missing private_key' unless json_key.key?('private_key')
[json_key['private_key'], json_key['client_email']]
end
module Google
module RPC
# Module Auth provides classes that provide Google-specific authentication
# used to access Google gRPC services.
module Auth
# Authenticates requests using Google's Service Account credentials.
# (cf https://developers.google.com/accounts/docs/OAuth2ServiceAccount)
class ServiceAccountCredentials < Signet::OAuth2::Client
TOKEN_CRED_URI = 'https://www.googleapis.com/oauth2/v3/token'
AUDIENCE = TOKEN_CRED_URI
# Initializes a ServiceAccountCredentials.
#
# @param scope [string|array] the scope(s) to access
# @param json_key_io [IO] an IO from which the JSON key can be read
def initialize(scope, json_key_io)
private_key, client_email = read_json_key(json_key_io)
super(token_credential_uri: TOKEN_CRED_URI,
audience: AUDIENCE,
scope: scope,
issuer: client_email,
signing_key: OpenSSL::PKey::RSA.new(private_key))
end
end
end
end
end

@ -0,0 +1,67 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'signet/oauth_2/client'
module Signet
# Signet::OAuth2 supports OAuth2 authentication.
module OAuth2
AUTH_METADATA_KEY = :Authorization
# Signet::OAuth2::Client creates an OAuth2 client
#
# Here client is re-opened to add the #apply and #apply! methods which
# update a hash map with the fetched authentication token
#
# Eventually, this change may be merged into signet itself, or some other
# package that provides Google-specific auth via signet, and this extension
# will be unnecessary.
class Client
# Updates a_hash updated with the authentication token
def apply!(a_hash, opts = {})
# fetch the access token there is currently not one, or if the client
# has expired
fetch_access_token!(opts) if access_token.nil? || expired?
a_hash[AUTH_METADATA_KEY] = "Bearer #{access_token}"
end
# Returns a clone of a_hash updated with the authentication token
def apply(a_hash, opts = {})
a_copy = a_hash.clone
apply!(a_copy, opts)
a_copy
end
# Returns a reference to the #apply method, suitable for passing as
# a closure
def updater_proc
lambda(&method(:apply))
end
end
end
end

@ -0,0 +1,163 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'faraday'
require 'spec_helper'
def build_json_response(payload)
[200,
{ 'Content-Type' => 'application/json; charset=utf-8' },
MultiJson.dump(payload)]
end
WANTED_AUTH_KEY = :Authorization
shared_examples 'apply/apply! are OK' do
# tests that use these examples need to define
#
# @client which should be an auth client
#
# @make_auth_stubs, which should stub out the expected http behaviour of the
# auth client
describe '#fetch_access_token' do
it 'should set access_token to the fetched value' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
@client.fetch_access_token!(connection: c)
expect(@client.access_token).to eq(token)
stubs.verify_stubbed_calls
end
end
describe '#apply!' do
it 'should update the target hash with fetched access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
@client.apply!(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(md).to eq(want)
stubs.verify_stubbed_calls
end
end
describe 'updater_proc' do
it 'should provide a proc that updates a hash with the access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
the_proc = @client.updater_proc
got = the_proc.call(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
end
end
describe '#apply' do
it 'should not update the original hash with the access token' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
@client.apply(md, connection: c)
want = { foo: 'bar' }
expect(md).to eq(want)
stubs.verify_stubbed_calls
end
it 'should add the token to the returned hash' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
end
it 'should not fetch a new token if the current is not expired' do
token = '1/abcdef1234567890'
stubs = make_auth_stubs with_access_token: token
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
n = 5 # arbitrary
n.times do |_t|
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{token}" }
expect(got).to eq(want)
end
stubs.verify_stubbed_calls
end
it 'should fetch a new token if the current one is expired' do
token_1 = '1/abcdef1234567890'
token_2 = '2/abcdef1234567890'
[token_1, token_2].each do |t|
stubs = make_auth_stubs with_access_token: t
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
md = { foo: 'bar' }
got = @client.apply(md, connection: c)
want = { :foo => 'bar', WANTED_AUTH_KEY => "Bearer #{t}" }
expect(got).to eq(want)
stubs.verify_stubbed_calls
@client.expires_at -= 3601 # default is to expire in 1hr
end
end
end
end

@ -0,0 +1,108 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'faraday'
require 'grpc/auth/compute_engine'
require 'spec_helper'
describe Google::RPC::Auth::GCECredentials do
MD_URI = '/computeMetadata/v1/instance/service-accounts/default/token'
GCECredentials = Google::RPC::Auth::GCECredentials
before(:example) do
@client = GCECredentials.new
end
def make_auth_stubs(with_access_token: '')
Faraday::Adapter::Test::Stubs.new do |stub|
stub.get(MD_URI) do |env|
headers = env[:request_headers]
expect(headers['Metadata-Flavor']).to eq('Google')
build_json_response(
'access_token' => with_access_token,
'token_type' => 'Bearer',
'expires_in' => 3600)
end
end
end
it_behaves_like 'apply/apply! are OK'
describe '#on_gce?' do
it 'should be true when Metadata-Flavor is Google' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/') do |_env|
[200,
{ 'Metadata-Flavor' => 'Google' },
'']
end
end
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
expect(GCECredentials.on_gce?(connection: c)).to eq(true)
stubs.verify_stubbed_calls
end
it 'should be false when Metadata-Flavor is not Google' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/') do |_env|
[200,
{ 'Metadata-Flavor' => 'NotGoogle' },
'']
end
end
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
expect(GCECredentials.on_gce?(connection: c)).to eq(false)
stubs.verify_stubbed_calls
end
it 'should be false if the response is not 200' do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('/') do |_env|
[404,
{ 'Metadata-Flavor' => 'Google' },
'']
end
end
c = Faraday.new do |b|
b.adapter(:test, stubs)
end
expect(GCECredentials.on_gce?(connection: c)).to eq(false)
stubs.verify_stubbed_calls
end
end
end

@ -0,0 +1,75 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'grpc/auth/service_account'
require 'jwt'
require 'multi_json'
require 'openssl'
require 'spec_helper'
describe Google::RPC::Auth::ServiceAccountCredentials do
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
cred_json = {
private_key_id: 'a_private_key_id',
private_key: @key.to_pem,
client_email: 'app@developer.gserviceaccount.com',
client_id: 'app.apps.googleusercontent.com',
type: 'service_account'
}
cred_json_text = MultiJson.dump(cred_json)
@client = Google::RPC::Auth::ServiceAccountCredentials.new(
'https://www.googleapis.com/auth/userinfo.profile',
StringIO.new(cred_json_text))
end
def make_auth_stubs(with_access_token: '')
Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/oauth2/v3/token') do |env|
params = Addressable::URI.form_unencode(env[:body])
_claim, _header = JWT.decode(params.assoc('assertion').last,
@key.public_key)
want = ['grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer']
expect(params.assoc('grant_type')).to eq(want)
build_json_response(
'access_token' => with_access_token,
'token_type' => 'Bearer',
'expires_in' => 3600
)
end
end
end
it_behaves_like 'apply/apply! are OK'
end

@ -0,0 +1,70 @@
# Copyright 2015, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.join(File.dirname(__FILE__)))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.uniq!
require 'apply_auth_examples'
require 'grpc/auth/signet'
require 'jwt'
require 'openssl'
require 'spec_helper'
describe Signet::OAuth2::Client do
before(:example) do
@key = OpenSSL::PKey::RSA.new(2048)
@client = Signet::OAuth2::Client.new(
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
scope: 'https://www.googleapis.com/auth/userinfo.profile',
issuer: 'app@example.com',
audience: 'https://accounts.google.com/o/oauth2/token',
signing_key: @key
)
end
def make_auth_stubs(with_access_token: '')
Faraday::Adapter::Test::Stubs.new do |stub|
stub.post('/o/oauth2/token') do |env|
params = Addressable::URI.form_unencode(env[:body])
_claim, _header = JWT.decode(params.assoc('assertion').last,
@key.public_key)
want = ['grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer']
expect(params.assoc('grant_type')).to eq(want)
build_json_response(
'access_token' => with_access_token,
'token_type' => 'Bearer',
'expires_in' => 3600
)
end
end
end
it_behaves_like 'apply/apply! are OK'
end

@ -29,8 +29,6 @@
require 'grpc'
FAKE_HOST='localhost:0'
def load_test_certs
test_root = File.join(File.dirname(__FILE__), 'testdata')
files = ['ca.pem', 'server1.key', 'server1.pem']
@ -38,6 +36,8 @@ def load_test_certs
end
describe GRPC::Core::Channel do
FAKE_HOST = 'localhost:0'
def create_test_cert
GRPC::Core::Credentials.new(load_test_certs[0])
end

@ -371,6 +371,6 @@ describe GRPC::ActiveCall do
end
def deadline
Time.now + 0.25 # in 0.25 seconds; arbitrary
Time.now + 1 # in 1 second; arbitrary
end
end

@ -27,10 +27,22 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
spec_dir = File.expand_path(File.dirname(__FILE__))
root_dir = File.expand_path(File.join(spec_dir, '..'))
lib_dir = File.expand_path(File.join(root_dir, 'lib'))
$LOAD_PATH.unshift(spec_dir)
$LOAD_PATH.unshift(lib_dir)
$LOAD_PATH.uniq!
require 'faraday'
require 'rspec'
require 'logging'
require 'rspec/logging_helper'
# Allow Faraday to support test stubs
Faraday::Adapter.load_middleware(:test)
# Configure RSpec to capture log messages for each test. The output from the
# logs will be stored in the @log_output variable. It is a StringIO instance.
RSpec.configure do |config|

@ -3,8 +3,8 @@
main() {
source grpc_docker.sh
test_cases=(large_unary empty_unary ping_pong client_streaming server_streaming)
clients=(cxx java go ruby)
servers=(cxx java go ruby)
clients=(cxx java go ruby node)
servers=(cxx java go ruby node)
for test_case in "${test_cases[@]}"
do
for client in "${clients[@]}"

Loading…
Cancel
Save