Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gapic-common): Honor universe domain in stubs #1008

Merged
merged 1 commit into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions gapic-common/lib/gapic/grpc/service_stub.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
require "gapic/grpc/service_stub/rpc_call"
require "gapic/grpc/service_stub/channel"
require "gapic/grpc/service_stub/channel_pool"

require "gapic/universe_domain_concerns"

module Gapic
##
Expand All @@ -31,14 +31,21 @@ module Gapic
# @return [Gapic::ServiceStub::ChannelPool] The instance of the ChannelPool class.
#
class ServiceStub
include UniverseDomainConcerns

attr_reader :grpc_stub
attr_reader :channel_pool

##
# Creates a Gapic gRPC stub object.
#
# @param grpc_stub_class [Class] gRPC stub class to create a new instance of.
# @param endpoint [String] The endpoint of the API.
# @param endpoint [String] The endpoint of the API. Overrides any endpoint_template.
# @param endpoint_template [String] The endpoint of the API, where the
# universe domain component of the hostname is marked by the string in
# the constant {UniverseDomainConcerns::ENDPOINT_SUBSTITUTION}.
# @param universe_domain [String] The universe domain in which calls should
# be made. Defaults to `googleapis.com`.
# @param credentials [Google::Auth::Credentials, Signet::OAuth2::Client, String, Hash, Proc,
# ::GRPC::Core::Channel, ::GRPC::Core::ChannelCredentials] Provides the means for authenticating requests made by
# the client. This parameter can be many types:
Expand All @@ -58,24 +65,32 @@ class ServiceStub
# @param channel_pool_config [::Gapic::ServiceStub:ChannelPool::Configuration] The configuration for channel
# pool. This argument will raise error when `credentials` is provided as a `::GRPC::Core::Channel`.
#
def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil,
interceptors: nil, channel_pool_config: nil
def initialize grpc_stub_class,
credentials:,
endpoint: nil,
endpoint_template: nil,
universe_domain: nil,
channel_args: nil,
interceptors: nil,
channel_pool_config: nil
raise ArgumentError, "grpc_stub_class is required" if grpc_stub_class.nil?
raise ArgumentError, "endpoint is required" if endpoint.nil?
raise ArgumentError, "credentials is required" if credentials.nil?

setup_universe_domain universe_domain: universe_domain,
endpoint: endpoint,
endpoint_template: endpoint_template,
credentials: credentials

@channel_pool = nil
@grpc_stub = nil
channel_args = Hash channel_args
interceptors = Array interceptors


if channel_pool_config && channel_pool_config.channel_count > 1
create_channel_pool grpc_stub_class, endpoint: endpoint, credentials: credentials,
create_channel_pool grpc_stub_class, endpoint: self.endpoint, credentials: self.credentials,
channel_args: channel_args, interceptors: interceptors,
channel_pool_config: channel_pool_config
else
create_grpc_stub grpc_stub_class, endpoint: endpoint, credentials: credentials,
create_grpc_stub grpc_stub_class, endpoint: self.endpoint, credentials: self.credentials,
channel_args: channel_args, interceptors: interceptors
end
end
Expand Down
33 changes: 25 additions & 8 deletions gapic-common/lib/gapic/rest/client_stub.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

require "googleauth"
require "gapic/rest/faraday_middleware"
require "gapic/universe_domain_concerns"
require "faraday/retry"

module Gapic
Expand All @@ -26,9 +27,16 @@ module Rest
# - store credentials and add auth information to the request
#
class ClientStub
include UniverseDomainConcerns

##
# Initializes with an endpoint and credentials
# @param endpoint [String] an endpoint for the service that this stub will send requests to
# @param endpoint [String] The endpoint of the API. Overrides any endpoint_template.
# @param endpoint_template [String] The endpoint of the API, where the
# universe domain component of the hostname is marked by the string in
# the constant {UniverseDomainConcerns::ENDPOINT_SUBSTITUTION}.
# @param universe_domain [String] The universe domain in which calls
# should be made. Defaults to `googleapis.com`.
# @param credentials [Google::Auth::Credentials]
# Credentials to send with calls in form of a googleauth credentials object.
# (see the [googleauth docs](https://googleapis.dev/ruby/googleauth/latest/index.html))
Expand All @@ -42,19 +50,28 @@ class ClientStub
#
# @yield [Faraday::Connection]
#
def initialize endpoint:, credentials:, numeric_enums: false, raise_faraday_errors: true
@endpoint = endpoint
@endpoint = "https://#{endpoint}" unless /^https?:/.match? endpoint
@endpoint = @endpoint.sub %r{/$}, ""
def initialize credentials:,
endpoint: nil,
endpoint_template: nil,
universe_domain: nil,
numeric_enums: false,
raise_faraday_errors: true
setup_universe_domain universe_domain: universe_domain,
endpoint: endpoint,
endpoint_template: endpoint_template,
credentials: credentials

endpoint_url = self.endpoint
endpoint_url = "https://#{endpoint_url}" unless /^https?:/.match? endpoint_url
endpoint_url = endpoint_url.sub %r{/$}, ""

@credentials = credentials
@numeric_enums = numeric_enums

@raise_faraday_errors = raise_faraday_errors

@connection = Faraday.new url: @endpoint do |conn|
@connection = Faraday.new url: endpoint_url do |conn|
conn.headers = { "Content-Type" => "application/json" }
conn.request :google_authorization, @credentials unless @credentials.is_a? ::Symbol
conn.request :google_authorization, self.credentials unless self.credentials.is_a? ::Symbol
conn.request :retry
conn.response :raise_error
conn.adapter :net_http
Expand Down
82 changes: 82 additions & 0 deletions gapic-common/lib/gapic/universe_domain_concerns.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright 2023 Google LLC
#
# 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
#
# https://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.

module Gapic
##
# A mixin module that provides methods for obtaining the effective universe
# domain, endpoint, and credentials for a stub. This is included in
# Grpc::ServiceStub and Rest::ClientStub.
#
module UniverseDomainConcerns
##
# A substitution string for the universe domain in an endpoint template
# @return [String]
#
ENDPOINT_SUBSTITUTION = "$UNIVERSE_DOMAIN$".freeze

##
# The effective endpoint
# @return [String]
#
attr_reader :endpoint

##
# The effective universe domain
# @return [String]
#
attr_reader :universe_domain

##
# The effective credentials
#
# @return [Google::Auth::Credentials, Signet::OAuth2::Client, Proc,
# ::GRPC::Core::Channel, ::GRPC::Core::ChannelCredentials]
#
attr_reader :credentials

protected

##
# @private
# Called from the stub constructor to populate the data.
#
def setup_universe_domain universe_domain: nil,
endpoint: nil,
endpoint_template: nil,
credentials: nil
raise ArgumentError, "endpoint or endpoint_template is required" if endpoint.nil? && endpoint_template.nil?
raise ArgumentError, "credentials is required" if credentials.nil?

# TODO: The env logic should live in google-cloud-env
universe_domain ||= ENV["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] || "googleapis.com"
endpoint ||= endpoint_template.sub ENDPOINT_SUBSTITUTION, universe_domain

if credentials.respond_to?(:universe_domain) && credentials.universe_domain != universe_domain
raise UniverseDomainMismatch,
"Universe domain is #{universe_domain} but credentials are in #{credentials.universe_domain}"
end

@universe_domain = universe_domain
@endpoint = endpoint
@credentials = credentials
end
end

##
# Raised when the configured universe domain does not match the universe
# domain of the credentials.
#
class UniverseDomainMismatch < ::Gapic::Common::Error
end
end
68 changes: 66 additions & 2 deletions gapic-common/test/gapic/grpc/service_stub_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,17 @@ def compose call_creds
end

FakeCredentials = Class.new Google::Auth::Credentials do
def initialize
def initialize universe_domain: nil
@custom_universe_domain = universe_domain || "googleapis.com"
end

def updater_proc
->{}
end

def universe_domain
@custom_universe_domain
end
end

FakeRpcCall = Class.new do
Expand Down Expand Up @@ -216,4 +221,63 @@ def test_call_rpc_without_channel_pool

assert_equal rpc_count, 1
end
end

class FakeGrpcStub
def initialize *args, **kwargs
end
end

def test_default_universe_domain
creds = FakeCredentials.new
service_stub = Gapic::ServiceStub.new FakeGrpcStub,
endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
credentials: creds
assert_equal "googleapis.com", service_stub.universe_domain
assert_equal "myservice.googleapis.com", service_stub.endpoint
end

def test_custom_universe_domain
creds = FakeCredentials.new universe_domain: "myuniverse.com"
service_stub = Gapic::ServiceStub.new FakeGrpcStub,
universe_domain: "myuniverse.com",
endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
credentials: creds
assert_equal "myuniverse.com", service_stub.universe_domain
assert_equal "myservice.myuniverse.com", service_stub.endpoint
end

def test_universe_domain_env
old_domain = ENV["GOOGLE_CLOUD_UNIVERSE_DOMAIN"]
ENV["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] = "myuniverse.com"
begin
creds = FakeCredentials.new universe_domain: "myuniverse.com"
service_stub = Gapic::ServiceStub.new FakeGrpcStub,
endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
credentials: creds
assert_equal "myuniverse.com", service_stub.universe_domain
assert_equal "myservice.myuniverse.com", service_stub.endpoint
ensure
ENV["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] = old_domain
end
end

def test_universe_domain_credentials_mismatch
creds = FakeCredentials.new universe_domain: "myuniverse.com"
assert_raises Gapic::UniverseDomainMismatch do
Gapic::ServiceStub.new FakeGrpcStub,
endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
credentials: creds
end
end

def test_endpoint_override
creds = FakeCredentials.new universe_domain: "myuniverse.com"
service_stub = Gapic::ServiceStub.new FakeGrpcStub,
universe_domain: "myuniverse.com",
endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
endpoint: "myservice.otheruniverse.com",
credentials: creds
assert_equal "myuniverse.com", service_stub.universe_domain
assert_equal "myservice.otheruniverse.com", service_stub.endpoint
end
end
4 changes: 4 additions & 0 deletions gapic-common/test/gapic/grpc/stub_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ def initialize
def updater_proc
->{}
end

def universe_domain
"googleapis.com"
end
end

def test_with_channel
Expand Down
59 changes: 59 additions & 0 deletions gapic-common/test/gapic/rest/client_stub_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,63 @@ def test_make_put_request_simple
client_stub.make_put_request uri: "/foo", body: "hello"
mock.verify
end

def test_default_universe_domain
client_stub = ::Gapic::Rest::ClientStub.new endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
credentials: :dummy_credentials
assert_equal "googleapis.com", client_stub.universe_domain
assert_equal "myservice.googleapis.com", client_stub.endpoint
end

def test_custom_universe_domain
client_stub = ::Gapic::Rest::ClientStub.new universe_domain: "myuniverse.com",
endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
credentials: :dummy_credentials
assert_equal "myuniverse.com", client_stub.universe_domain
assert_equal "myservice.myuniverse.com", client_stub.endpoint
end

def test_universe_domain_env
old_domain = ENV["GOOGLE_CLOUD_UNIVERSE_DOMAIN"]
ENV["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] = "myuniverse.com"
begin
client_stub = ::Gapic::Rest::ClientStub.new endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
credentials: :dummy_credentials
assert_equal "myuniverse.com", client_stub.universe_domain
assert_equal "myservice.myuniverse.com", client_stub.endpoint
ensure
ENV["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] = old_domain
end
end

def test_endpoint_override
client_stub = ::Gapic::Rest::ClientStub.new universe_domain: "myuniverse.com",
endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
endpoint: "myservice.otheruniverse.com",
credentials: :dummy_credentials
assert_equal "myuniverse.com", client_stub.universe_domain
assert_equal "myservice.otheruniverse.com", client_stub.endpoint
end

FakeCredentials = Class.new Google::Auth::Credentials do
def initialize universe_domain: nil
@custom_universe_domain = universe_domain || "googleapis.com"
end

def updater_proc
->{}
end

def universe_domain
@custom_universe_domain
end
end

def test_universe_domain_credentials_mismatch
creds = FakeCredentials.new universe_domain: "myuniverse.com"
assert_raises Gapic::UniverseDomainMismatch do
::Gapic::Rest::ClientStub.new endpoint_template: "myservice.$UNIVERSE_DOMAIN$",
credentials: creds
end
end
end