Add OAuth interop tests to Python

Also adds the commands to the grpc_docker shell library script to
support running in docker.
diff --git a/src/python/interop/interop/_interop_test_case.py b/src/python/interop/interop/_interop_test_case.py
index fec8f19..cd6a574 100644
--- a/src/python/interop/interop/_interop_test_case.py
+++ b/src/python/interop/interop/_interop_test_case.py
@@ -40,16 +40,16 @@
   """
 
   def testEmptyUnary(self):
-    methods.TestCase.EMPTY_UNARY.test_interoperability(self.stub)
+    methods.TestCase.EMPTY_UNARY.test_interoperability(self.stub, None)
 
   def testLargeUnary(self):
-    methods.TestCase.LARGE_UNARY.test_interoperability(self.stub)
+    methods.TestCase.LARGE_UNARY.test_interoperability(self.stub, None)
 
   def testServerStreaming(self):
-    methods.TestCase.SERVER_STREAMING.test_interoperability(self.stub)
+    methods.TestCase.SERVER_STREAMING.test_interoperability(self.stub, None)
 
   def testClientStreaming(self):
-    methods.TestCase.CLIENT_STREAMING.test_interoperability(self.stub)
+    methods.TestCase.CLIENT_STREAMING.test_interoperability(self.stub, None)
 
   def testPingPong(self):
-    methods.TestCase.PING_PONG.test_interoperability(self.stub)
+    methods.TestCase.PING_PONG.test_interoperability(self.stub, None)
diff --git a/src/python/interop/interop/client.py b/src/python/interop/interop/client.py
index 85a0dcd..bae5e17 100644
--- a/src/python/interop/interop/client.py
+++ b/src/python/interop/interop/client.py
@@ -30,6 +30,7 @@
 """The Python implementation of the GRPC interoperability test client."""
 
 import argparse
+from oauth2client import client as oauth2client_client
 
 from grpc.early_adopter import implementations
 
@@ -44,9 +45,6 @@
   parser.add_argument(
       '--server_host', help='the host to which to connect', type=str)
   parser.add_argument(
-      '--server_host_override',
-      help='the server host to which to claim to connect', type=str)
-  parser.add_argument(
       '--server_port', help='the port to which to connect', type=int)
   parser.add_argument(
       '--test_case', help='the test case to execute', type=str)
@@ -56,10 +54,25 @@
   parser.add_argument(
       '--use_test_ca', help='replace platform root CAs with ca.pem',
       action='store_true')
+  parser.add_argument(
+      '--server_host_override',
+      help='the server host to which to claim to connect', type=str)
+  parser.add_argument('--oauth_scope', help='scope for OAuth tokens', type=str)
+  parser.add_argument(
+      '--default_service_account',
+      help='email address of the default service account', type=str)
   return parser.parse_args()
 
+def _oauth_access_token(args):
+  credentials = client.GoogleCredentials.get_application_default()
+  scoped_credentials = credentials.create_scoped([args.oauth_scope])
+  return scoped_credentials.get_access_token().access_token
 
 def _stub(args):
+  if args.oauth_scope:
+    metadata_transformer = lambda x: [('Authorization', 'Bearer %s' % _oauth_access_token(args))]
+  else:
+    metadata_transformer = lambda x: []
   if args.use_tls:
     if args.use_test_ca:
       root_certificates = resources.test_root_certificates()
@@ -68,7 +81,8 @@
 
     stub = implementations.stub(
         methods.SERVICE_NAME, methods.CLIENT_METHODS, args.server_host,
-        args.server_port, secure=True, root_certificates=root_certificates,
+        args.server_port, metadata_transformer=metadata_transformer,
+        secure=True, root_certificates=root_certificates,
         server_host_override=args.server_host_override)
   else:
     stub = implementations.stub(
@@ -89,7 +103,7 @@
   args = _args()
   stub = _stub(args)
   test_case = _test_case_from_arg(args.test_case)
-  test_case.test_interoperability(stub)
+  test_case.test_interoperability(stub, args)
 
 
 if __name__ == '__main__':
diff --git a/src/python/interop/interop/methods.py b/src/python/interop/interop/methods.py
index 79550a3..c69771d 100644
--- a/src/python/interop/interop/methods.py
+++ b/src/python/interop/interop/methods.py
@@ -30,8 +30,12 @@
 """Implementations of interoperability test methods."""
 
 import enum
+import json
+import os
 import threading
 
+from oauth2client import client as oauth2client_client
+
 from grpc.framework.alpha import utilities
 
 from interop import empty_pb2
@@ -150,6 +154,23 @@
 }
 
 
+def _large_unary_common_behavior(stub, fill_username, fill_oauth_scope):
+  with stub:
+    request = messages_pb2.SimpleRequest(
+        response_type=messages_pb2.COMPRESSABLE, response_size=314159,
+        payload=messages_pb2.Payload(body=b'\x00' * 271828),
+        fill_username=fill_username, fill_oauth_scope=fill_oauth_scope)
+    response_future = stub.UnaryCall.async(request, _TIMEOUT)
+    response = response_future.result()
+    if response.payload.type is not messages_pb2.COMPRESSABLE:
+      raise ValueError(
+          'response payload type is "%s"!' % type(response.payload.type))
+    if len(response.payload.body) != 314159:
+      raise ValueError(
+          'response body of incorrect size %d!' % len(response.payload.body))
+    return response
+
+
 def _empty_unary(stub):
   with stub:
     response = stub.EmptyCall(empty_pb2.Empty(), _TIMEOUT)
@@ -159,18 +180,7 @@
 
 
 def _large_unary(stub):
-  with stub:
-    request = messages_pb2.SimpleRequest(
-        response_type=messages_pb2.COMPRESSABLE, response_size=314159,
-        payload=messages_pb2.Payload(body=b'\x00' * 271828))
-    response_future = stub.UnaryCall.async(request, _TIMEOUT)
-    response = response_future.result()
-    if response.payload.type is not messages_pb2.COMPRESSABLE:
-      raise ValueError(
-          'response payload type is "%s"!' % type(response.payload.type))
-    if len(response.payload.body) != 314159:
-      raise ValueError(
-          'response body of incorrect size %d!' % len(response.payload.body))
+  _large_unary_common_behavior(stub, False, False)
 
 
 def _client_streaming(stub):
@@ -266,6 +276,28 @@
     pipe.close()
 
 
+def _compute_engine_creds(stub, args):
+  response = _large_unary_common_behavior(stub, True, True)
+  if args.default_service_account != response.username:
+    raise ValueError(
+        'expected username %s, got %s' % (args.default_service_account,
+                                          response.username))
+
+
+def _service_account_creds(stub, args):
+  json_key_filename = os.environ[
+      oauth2client_client.GOOGLE_APPLICATION_CREDENTIALS]
+  wanted_email = json.load(open(json_key_filename, 'rb'))['client_email']
+  response = _large_unary_common_behavior(stub, True, True)
+  if wanted_email != response.username:
+    raise ValueError(
+        'expected username %s, got %s' % (wanted_email, response.username))
+  if response.oauth_scope in args.oauth_scope:
+    raise ValueError(
+        'expected to find oauth scope "%s" in received "%s"' %
+            (response.oauth_scope, args.oauth_scope))
+
+
 @enum.unique
 class TestCase(enum.Enum):
   EMPTY_UNARY = 'empty_unary'
@@ -273,8 +305,10 @@
   SERVER_STREAMING = 'server_streaming'
   CLIENT_STREAMING = 'client_streaming'
   PING_PONG = 'ping_pong'
+  COMPUTE_ENGINE_CREDS = 'compute_engine_creds'
+  SERVICE_ACCOUNT_CREDS = 'service_account_creds'
 
-  def test_interoperability(self, stub):
+  def test_interoperability(self, stub, args):
     if self is TestCase.EMPTY_UNARY:
       _empty_unary(stub)
     elif self is TestCase.LARGE_UNARY:
@@ -285,5 +319,9 @@
       _client_streaming(stub)
     elif self is TestCase.PING_PONG:
       _ping_pong(stub)
+    elif self is TestCase.COMPUTE_ENGINE_CREDS:
+      _compute_engine_creds(stub, args)
+    elif self is TestCase.SERVICE_ACCOUNT_CREDS:
+      _service_account_creds(stub, args)
     else:
       raise NotImplementedError('Test case "%s" not implemented!' % self.name)
diff --git a/src/python/interop/setup.py b/src/python/interop/setup.py
index 7a32992..502fcbe 100644
--- a/src/python/interop/setup.py
+++ b/src/python/interop/setup.py
@@ -45,7 +45,7 @@
         'credentials/server1.pem',]
 }
 
-_INSTALL_REQUIRES = ['grpcio>=0.4.0a4']
+_INSTALL_REQUIRES = ['oauth2client>=1.4.7', 'grpcio>=0.4.0a4']
 
 setuptools.setup(
     name='interop',
diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh
index dc48098..4063429 100755
--- a/tools/gce_setup/grpc_docker.sh
+++ b/tools/gce_setup/grpc_docker.sh
@@ -1041,6 +1041,35 @@
   echo $the_cmd
 }
 
+# constructs the full dockerized python service_account auth interop test cmd.
+#
+# call-seq:
+#   flags= .... # generic flags to include the command
+#   cmd=$($grpc_gen_test_cmd $flags)
+grpc_cloud_prod_auth_service_account_creds_gen_python_cmd() {
+  local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c";
+  local gfe_flags=$(_grpc_prod_gfe_flags)
+  local added_gfe_flags=$(_grpc_default_creds_test_flags)
+  local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem"
+  env_prefix+=" GOOGLE_APPLICATION_CREDENTIALS=/service_account/stubbyCloudTestingTest-7dd63462c60c.json"
+  local the_cmd="$cmd_prefix '$env_prefix python -B -m interop.client --use_tls $gfe_flags $added_gfe_flags $@'"
+  echo $the_cmd
+}
+
+# constructs the full dockerized python gce auth interop test cmd.
+#
+# call-seq:
+#   flags= .... # generic flags to include the command
+#   cmd=$($grpc_gen_test_cmd $flags)
+grpc_cloud_prod_auth_compute_engine_creds_gen_python_cmd() {
+  local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c";
+  local gfe_flags=$(_grpc_prod_gfe_flags)
+  local added_gfe_flags=$(_grpc_gce_test_flags)
+  local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem"
+  local the_cmd="$cmd_prefix '$env_prefix python -B -m interop.client --use_tls $gfe_flags $added_gfe_flags $@'"
+  echo $the_cmd
+}
+
 # constructs the full dockerized java interop test cmd.
 #
 # call-seq: