Add interop cloud to prod test for GoogleDefaultCredentials

pull/17213/head
Alexander Polcyn 6 years ago
parent 1e1d4c2621
commit a4e9f33b85
  1. 38
      doc/interop-test-descriptions.md
  2. 6
      test/cpp/interop/client.cc
  3. 19
      test/cpp/interop/interop_client.cc
  4. 2
      test/cpp/interop/interop_client.h
  5. 3
      tools/internal_ci/macos/grpc_interop_toprod.sh
  6. 93
      tools/run_tests/run_interop_tests.py

@ -679,6 +679,44 @@ Client asserts:
by the auth library. The client can optionally check the username matches the
email address in the key file.
### google_default_credentials
Similar to the other auth tests, this test should only be run against prod
servers. Different from some of the other auth tests however, this test
may be also run from outside of GCP.
This test verifies unary calls succeed when the client uses
GoogleDefaultCredentials. The path to a service account key file in the
GOOGLE_APPLICATION_CREDENTIALS environment variable may or may not be
provided by the test runner. For example, the test runner might set
this environment when outside of GCP but keep it unset when on GCP.
The test uses `--default_service_account` with GCE service account email.
Server features:
* [UnaryCall][]
* [Echo Authenticated Username][]
Procedure:
1. Client configures the channel to use GoogleDefaultCredentials
* Note: the term `GoogleDefaultCredentials` within the context
of this test description refers to an API which encapsulates
both "transport credentials" and "call credentials" and which
is capable of transport creds auto-selection (including ALTS).
Similar APIs involving only auto-selection of OAuth mechanisms
might work for this test but aren't the intended subjects.
2. Client calls UnaryCall with:
```
{
fill_username: true
}
```
Client asserts:
* call was successful
* received SimpleResponse.username matches the value of
`--default_service_account`
### custom_metadata

@ -54,6 +54,7 @@ DEFINE_string(
"custom_metadata: server will echo custom metadata;\n"
"empty_stream : bi-di stream with no request/response;\n"
"empty_unary : empty (zero bytes) request and response;\n"
"google_default_credentials: large unary using GDC;\n"
"half_duplex : half-duplex streaming;\n"
"jwt_token_creds: large_unary with JWT token auth;\n"
"large_unary : single request and (large) response;\n"
@ -151,6 +152,11 @@ int main(int argc, char** argv) {
std::bind(&grpc::testing::InteropClient::DoPerRpcCreds, &client,
GetServiceAccountJsonKey());
}
if (FLAGS_custom_credentials_type == "google_default_credentials") {
actions["google_default_credentials"] =
std::bind(&grpc::testing::InteropClient::DoGoogleDefaultCredentials,
&client, FLAGS_default_service_account);
}
actions["status_code_and_message"] =
std::bind(&grpc::testing::InteropClient::DoStatusWithMessage, &client);
actions["custom_metadata"] =

@ -294,6 +294,25 @@ bool InteropClient::DoJwtTokenCreds(const grpc::string& username) {
return true;
}
bool InteropClient::DoGoogleDefaultCredentials(
const grpc::string& default_service_account) {
gpr_log(GPR_DEBUG,
"Sending a large unary rpc with GoogleDefaultCredentials...");
SimpleRequest request;
SimpleResponse response;
request.set_fill_username(true);
if (!PerformLargeUnary(&request, &response)) {
return false;
}
gpr_log(GPR_DEBUG, "Got username %s", response.username().c_str());
GPR_ASSERT(!response.username().empty());
GPR_ASSERT(response.username().c_str() == default_service_account);
gpr_log(GPR_DEBUG, "Large unary rpc with GoogleDefaultCredentials done.");
return true;
}
bool InteropClient::DoLargeUnary() {
gpr_log(GPR_DEBUG, "Sending a large unary rpc...");
SimpleRequest request;

@ -89,6 +89,8 @@ class InteropClient {
const grpc::string& oauth_scope);
// username is a string containing the user email
bool DoPerRpcCreds(const grpc::string& json_key);
// username is the GCE default service account email
bool DoGoogleDefaultCredentials(const grpc::string& username);
private:
class ServiceStub {

@ -30,7 +30,8 @@ export GRPC_DEFAULT_SSL_ROOTS_FILE_PATH="$(pwd)/etc/roots.pem"
# building all languages in the same working copy can also lead to conflicts
# due to different compilation flags
tools/run_tests/run_interop_tests.py -l c++ \
--cloud_to_prod --cloud_to_prod_auth --prod_servers default gateway_v4 \
--cloud_to_prod --cloud_to_prod_auth --on_gce=false \
--prod_servers default gateway_v4 \
--service_account_key_file="${KOKORO_GFILE_DIR}/GrpcTesting-726eb1347f15.json" \
--skip_compute_engine_creds --internal_ci -t -j 4 || FAILED="true"

@ -65,6 +65,12 @@ _SKIP_ADVANCED = [
_SKIP_SPECIAL_STATUS_MESSAGE = ['special_status_message']
_GOOGLE_DEFAULT_CREDS_TEST_CASE = 'google_default_credentials'
_SKIP_GOOGLE_DEFAULT_CREDS = [
_GOOGLE_DEFAULT_CREDS_TEST_CASE,
]
_TEST_TIMEOUT = 3 * 60
# disable this test on core-based languages,
@ -129,7 +135,7 @@ class CSharpLanguage:
return {}
def unimplemented_test_cases(self):
return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@ -158,7 +164,7 @@ class CSharpCoreCLRLanguage:
return {}
def unimplemented_test_cases(self):
return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@ -188,7 +194,7 @@ class DartLanguage:
return {}
def unimplemented_test_cases(self):
return _SKIP_COMPRESSION + _SKIP_SPECIAL_STATUS_MESSAGE
return _SKIP_COMPRESSION + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION + _SKIP_SPECIAL_STATUS_MESSAGE
@ -223,7 +229,7 @@ class JavaLanguage:
return {}
def unimplemented_test_cases(self):
return []
return _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@ -248,7 +254,7 @@ class JavaOkHttpClient:
return {}
def unimplemented_test_cases(self):
return _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
return _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def __str__(self):
return 'javaokhttp'
@ -279,7 +285,7 @@ class GoLanguage:
return {}
def unimplemented_test_cases(self):
return _SKIP_COMPRESSION
return _SKIP_COMPRESSION + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@ -309,7 +315,7 @@ class Http2Server:
return {}
def unimplemented_test_cases(self):
return _TEST_CASES + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
return _TEST_CASES + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _TEST_CASES
@ -339,7 +345,7 @@ class Http2Client:
return {}
def unimplemented_test_cases(self):
return _TEST_CASES + _SKIP_SPECIAL_STATUS_MESSAGE
return _TEST_CASES + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _TEST_CASES
@ -376,7 +382,7 @@ class NodeLanguage:
return {}
def unimplemented_test_cases(self):
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@ -406,7 +412,7 @@ class NodePureJSLanguage:
return {}
def unimplemented_test_cases(self):
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return []
@ -431,7 +437,7 @@ class PHPLanguage:
return {}
def unimplemented_test_cases(self):
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return []
@ -456,7 +462,7 @@ class PHP7Language:
return {}
def unimplemented_test_cases(self):
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return []
@ -491,7 +497,7 @@ class ObjcLanguage:
# cmdline argument. Here we return all but one test cases as unimplemented,
# and depend upon ObjC test's behavior that it runs all cases even when
# we tell it to run just one.
return _TEST_CASES[1:] + _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
return _TEST_CASES[1:] + _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@ -526,7 +532,7 @@ class RubyLanguage:
return {}
def unimplemented_test_cases(self):
return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE
return _SKIP_SERVER_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_SPECIAL_STATUS_MESSAGE + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@ -571,7 +577,7 @@ class PythonLanguage:
}
def unimplemented_test_cases(self):
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING
return _SKIP_COMPRESSION + _SKIP_DATA_FRAME_PADDING + _SKIP_GOOGLE_DEFAULT_CREDS
def unimplemented_test_cases_server(self):
return _SKIP_COMPRESSION
@ -614,8 +620,11 @@ _TEST_CASES = [
]
_AUTH_TEST_CASES = [
'compute_engine_creds', 'jwt_token_creds', 'oauth2_auth_token',
'per_rpc_creds'
'compute_engine_creds',
'jwt_token_creds',
'oauth2_auth_token',
'per_rpc_creds',
_GOOGLE_DEFAULT_CREDS_TEST_CASE,
]
_HTTP2_TEST_CASES = ['tls', 'framing']
@ -714,7 +723,7 @@ def compute_engine_creds_required(language, test_case):
return False
def auth_options(language, test_case, service_account_key_file=None):
def auth_options(language, test_case, on_gce, service_account_key_file=None):
"""Returns (cmdline, env) tuple with cloud_to_prod_auth test options."""
language = str(language)
@ -728,9 +737,6 @@ def auth_options(language, test_case, service_account_key_file=None):
key_file_arg = '--service_account_key_file=%s' % service_account_key_file
default_account_arg = '--default_service_account=830293263384-compute@developer.gserviceaccount.com'
# TODO: When using google_default_credentials outside of cloud-to-prod, the environment variable
# 'GOOGLE_APPLICATION_CREDENTIALS' needs to be set for the test case
# 'jwt_token_creds' to work.
if test_case in ['jwt_token_creds', 'per_rpc_creds', 'oauth2_auth_token']:
if language in [
'csharp', 'csharpcoreclr', 'node', 'php', 'php7', 'python',
@ -750,6 +756,11 @@ def auth_options(language, test_case, service_account_key_file=None):
if test_case == 'compute_engine_creds':
cmdargs += [oauth_scope_arg, default_account_arg]
if test_case == _GOOGLE_DEFAULT_CREDS_TEST_CASE:
if not on_gce:
env['GOOGLE_APPLICATION_CREDENTIALS'] = service_account_key_file
cmdargs += [default_account_arg]
return (cmdargs, env)
@ -767,6 +778,7 @@ def cloud_to_prod_jobspec(language,
test_case,
server_host_nickname,
server_host,
on_gce,
docker_image=None,
auth=False,
manual_cmd_log=None,
@ -793,7 +805,7 @@ def cloud_to_prod_jobspec(language,
cmdargs = cmdargs + transport_security_options
environ = dict(language.cloud_to_prod_env(), **language.global_env())
if auth:
auth_cmdargs, auth_env = auth_options(language, test_case,
auth_cmdargs, auth_env = auth_options(language, test_case, on_gce,
service_account_key_file)
cmdargs += auth_cmdargs
environ.update(auth_env)
@ -1071,6 +1083,12 @@ argp.add_argument(
action='store_const',
const=True,
help='Run cloud_to_prod_auth tests.')
argp.add_argument(
'--on_gce',
default=True,
action='store_const',
const=True,
help='Whether or not this test script is running on GCE.')
argp.add_argument(
'--prod_servers',
choices=prod_servers.keys(),
@ -1326,6 +1344,7 @@ try:
test_case,
server_host_nickname,
prod_servers[server_host_nickname],
on_gce=args.on_gce,
docker_image=docker_images.get(str(language)),
manual_cmd_log=client_manual_cmd_log,
service_account_key_file=args.
@ -1340,6 +1359,7 @@ try:
test_case,
server_host_nickname,
prod_servers[server_host_nickname],
on_gce=args.on_gce,
docker_image=docker_images.get(
str(language)),
manual_cmd_log=client_manual_cmd_log,
@ -1356,6 +1376,7 @@ try:
test_case,
server_host_nickname,
prod_servers[server_host_nickname],
on_gce=args.on_gce,
docker_image=docker_images.get(str(http2Interop)),
manual_cmd_log=client_manual_cmd_log,
service_account_key_file=args.service_account_key_file,
@ -1374,36 +1395,22 @@ try:
not compute_engine_creds_required(
language, test_case)):
if not test_case in language.unimplemented_test_cases():
tls_test_job = cloud_to_prod_jobspec(
transport_security = 'tls'
if test_case == _GOOGLE_DEFAULT_CREDS_TEST_CASE:
transport_security = 'google_default_credentials'
test_job = cloud_to_prod_jobspec(
language,
test_case,
server_host_nickname,
prod_servers[server_host_nickname],
on_gce=args.on_gce,
docker_image=docker_images.get(str(language)),
auth=True,
manual_cmd_log=client_manual_cmd_log,
service_account_key_file=args.
service_account_key_file,
transport_security='tls')
jobs.append(tls_test_job)
if str(language) in [
'go'
]: # Add more languages to the list to turn on tests.
google_default_creds_test_job = cloud_to_prod_jobspec(
language,
test_case,
server_host_nickname,
prod_servers[server_host_nickname],
docker_image=docker_images.get(
str(language)),
auth=True,
manual_cmd_log=client_manual_cmd_log,
service_account_key_file=args.
service_account_key_file,
transport_security=
'google_default_credentials')
jobs.append(google_default_creds_test_job)
transport_security=transport_security)
jobs.append(test_job)
for server in args.override_server:
server_name = server[0]
(server_host, server_port) = server[1].split(':')

Loading…
Cancel
Save