├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── cloudprober ├── MANIFEST.in ├── cloudprober.cfg ├── codegen.sh ├── grpc_gcp_prober │ ├── __init__.py │ ├── firestore_probes.py │ ├── prober.py │ ├── spanner.grpc.config │ ├── spanner_probes.py │ ├── stackdriver_util.py │ ├── tracer.py │ └── versions.py ├── requirements.txt ├── setup.py ├── setup.sh ├── template │ └── __init__.py └── tests │ ├── firestore_probes_test.py │ ├── run_tests.sh │ ├── spanner_probes_test.py │ └── test_requirements.txt ├── doc ├── cloudprober-guide.md └── gRPC-client-user-guide.md ├── firestore └── examples │ └── end2end │ ├── doc │ └── .gitignore │ └── src │ ├── .gitignore │ ├── BatchGetDocuments.py │ ├── BeginTransaction.py │ ├── Commit.py │ ├── CreateDocument.py │ ├── CreateIndex.py │ ├── DeleteDocument.py │ ├── DeleteIndex.py │ ├── GetDocument.py │ ├── GetIndex.py │ ├── License │ ├── ListCollectionIds.py │ ├── ListDocuments.py │ ├── ListIndex.py │ ├── README │ ├── Rollback.py │ ├── RunQuery.py │ ├── UpdateDocument.py │ ├── Write.py │ ├── grpc.json │ └── grpcCtrl ├── kokoro └── presubmit.cfg ├── src ├── .gitignore ├── CHANGELOG.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── grpc_gcp.proto ├── grpc_gcp │ ├── __init__.py │ ├── _channel.py │ └── proto │ │ ├── __init__.py │ │ └── grpc_gcp_pb2.py ├── requirements.txt ├── setup.cfg ├── setup.py └── setup.sh ├── template ├── __init__.py └── version.py └── tests ├── .gitignore ├── MANIFEST.in ├── benchmark.sh ├── codegen.sh ├── grpc_gcp_test ├── __init__.py ├── benchmark │ ├── __init__.py │ ├── spanner.grpc.config │ └── spanner_benchmark.py ├── integration │ ├── __init__.py │ ├── spanner.grpc.config │ └── spanner_test.py ├── stress │ ├── __init__.py │ ├── client.py │ ├── spanner.grpc.config │ ├── spanner_test_cases.py │ └── stackdriver_util.py └── unit │ ├── __init__.py │ ├── _auth_context_test.py │ ├── _channel_connectivity_test.py │ ├── _channel_ready_future_test.py │ ├── _empty_message_test.py │ ├── _exit_scenarios.py │ ├── _exit_test.py │ ├── _metadata_code_details_test.py │ ├── _metadata_test.py │ ├── _reconnect_test.py │ ├── _resource_exhausted_test.py │ ├── _rpc_test.py │ ├── _thread_pool.py │ ├── credentials │ ├── README.md │ ├── ca.pem │ ├── certificate_hierarchy_1 │ │ ├── certs │ │ │ └── ca.cert.pem │ │ └── intermediate │ │ │ ├── certs │ │ │ ├── client.cert.pem │ │ │ ├── intermediate.cert.pem │ │ │ └── localhost-1.cert.pem │ │ │ └── private │ │ │ ├── client.key.pem │ │ │ └── localhost-1.key.pem │ ├── certificate_hierarchy_2 │ │ ├── certs │ │ │ └── ca.cert.pem │ │ └── intermediate │ │ │ ├── certs │ │ │ ├── client.cert.pem │ │ │ ├── intermediate.cert.pem │ │ │ └── localhost-1.cert.pem │ │ │ └── private │ │ │ ├── client.key.pem │ │ │ └── localhost-1.key.pem │ ├── server1.key │ └── server1.pem │ ├── framework │ ├── __init__.py │ ├── common │ │ ├── __init__.py │ │ ├── test_constants.py │ │ ├── test_control.py │ │ └── test_coverage.py │ └── foundation │ │ ├── __init__.py │ │ ├── _logging_pool_test.py │ │ └── stream_testing.py │ ├── resources.py │ └── test_common.py ├── integration.sh ├── requirements.txt ├── setup.py ├── setup.sh ├── stress.sh └── unit.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # python compiled objects. 2 | *.pyc 3 | 4 | # IDE folders. 5 | .vscode/ 6 | 7 | # generated code. 8 | tests/google 9 | cloudprober/google 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/googleapis"] 2 | path = third_party/googleapis 3 | url = https://github.com/googleapis/googleapis.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gRPC for GCP extensions 2 | 3 | Copyright 2018 4 | [The gRPC Authors](https://github.com/grpc/grpc/blob/master/AUTHORS) 5 | 6 | ## About This Repository 7 | 8 | This repo is created to support GCP specific extensions for gRPC. To use the extension features, please refer to [grpcio-gcp](src). 9 | 10 | This repo also contains supporting infrastructures such as end2end tests and benchmarks for accessing cloud APIs with gRPC client libraries. 11 | 12 | ## Testing 13 | 14 | Download from github 15 | 16 | ```sh 17 | $ git clone https://github.com/GoogleCloudPlatform/grpc-gcp-python.git 18 | $ cd grpc-gcp-python 19 | $ git submodule update --init --recursive 20 | ``` 21 | 22 | Setup credentials. See [Getting Started With Authentication](https://cloud.google.com/docs/authentication/getting-started) for more details. 23 | 24 | ```sh 25 | $ export GOOGLE_APPLICATION_CREDENTIALS=path/to/key.json 26 | ``` 27 | 28 | Install the gRPC-GCP extension 29 | 30 | ```sh 31 | $ src/setup.sh 32 | ``` 33 | 34 | Install the gRPC-GCP extension test & benchmark suite 35 | 36 | ```sh 37 | $ tests/setup.sh 38 | ``` 39 | 40 | Run end to end integration tests. 41 | 42 | ```sh 43 | $ tests/integration.sh 44 | ``` 45 | 46 | Run end to end benchmark with gRPC-GCP extension. 47 | 48 | ```sh 49 | $ tests/benchmark.sh --gcp 50 | ``` 51 | 52 | Run end to end benchmark without gRPC-GCP extension. For comparison purspose. 53 | 54 | ```sh 55 | $ tests/benchmark.sh 56 | ``` 57 | 58 | Run grpc channel unit tests. 59 | 60 | ```sh 61 | $ tests/unit.sh 62 | ``` 63 | 64 | Run stress test against grpc client calls with weights. 65 | 66 | ```sh 67 | $ tests/stress.sh --gcp --weighted_cases 'list_sessions:100' 68 | ``` 69 | 70 | -------------------------------------------------------------------------------- /cloudprober/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include grpc_gcp_prober *.py *.config 2 | recursive-include google *.py 3 | global-exclude *.pyc 4 | -------------------------------------------------------------------------------- /cloudprober/cloudprober.cfg: -------------------------------------------------------------------------------- 1 | probe { 2 | type: EXTERNAL 3 | name: "spanner" 4 | interval_msec: 1800000 5 | timeout_msec: 30000 6 | targets { dummy_targets {} } # No targets for external probe 7 | external_probe { 8 | mode: ONCE 9 | command: "python -m grpc_gcp_prober.prober --api=spanner" 10 | } 11 | } 12 | 13 | probe { 14 | type: EXTERNAL 15 | name: "spanner_gcp_ext" 16 | interval_msec: 1800000 17 | timeout_msec: 30000 18 | targets { dummy_targets {} } # No targets for external probe 19 | external_probe { 20 | mode: ONCE 21 | command: "python -m grpc_gcp_prober.prober --api=spanner --extension=True" 22 | } 23 | } 24 | 25 | probe { 26 | type: EXTERNAL 27 | name: "firestore" 28 | interval_msec: 1800000 29 | timeout_msec: 30000 30 | targets { dummy_targets {} } # No targets for external probe 31 | external_probe { 32 | mode: ONCE 33 | command: "python -m grpc_gcp_prober.prober --api=firestore" 34 | } 35 | } 36 | 37 | surfacer { 38 | type: STACKDRIVER 39 | name: "stackdriver" 40 | stackdriver_surfacer { 41 | monitoring_url: "custom.googleapis.com/cloudprober/" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cloudprober/codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | rm -rf google 4 | for p in $(find ../third_party/googleapis/google -type f -name '*.proto'); do 5 | python \ 6 | -m grpc_tools.protoc \ 7 | -I ../third_party/googleapis/ \ 8 | --python_out=. \ 9 | --grpc_python_out=. \ 10 | "${p}" 11 | done 12 | 13 | for d in $(find google -type d); do 14 | touch "${d}/__init__.py" 15 | done 16 | 17 | cp -f template/__init__.py google -------------------------------------------------------------------------------- /cloudprober/grpc_gcp_prober/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /cloudprober/grpc_gcp_prober/firestore_probes.py: -------------------------------------------------------------------------------- 1 | """Source code for the Firestore probes the cloudprober will execute. 2 | 3 | Each method implements Firestore grpc client calls to it's grpc backend service. 4 | The latency for each client call will be output to stackdriver as metrics. Note 5 | that the metric output needs to be in a format of "key value" string. 6 | e.g. "read_latency_ms 100" 7 | """ 8 | 9 | import os 10 | from google.cloud.firestore_v1beta1.proto import firestore_pb2 11 | 12 | from tracer import initialize_tracer 13 | 14 | _PARENT_RESOURCE = os.environ['PARENT_RESOURCE'] 15 | _FIRESTORE_TARGET = os.environ['FIRESTORE_TARGET'] 16 | 17 | 18 | def _documents(stub): 19 | """Probes to test ListDocuments grpc call from Firestore stub. 20 | 21 | Args: 22 | stub: An object of FirestoreStub. 23 | """ 24 | _documents_tracer = initialize_tracer() 25 | with _documents_tracer.span(name='_documents') as root_span: 26 | root_span.add_annotation('endpoint info available', endpoint=_FIRESTORE_TARGET) 27 | list_document_request = firestore_pb2.ListDocumentsRequest( 28 | parent=_PARENT_RESOURCE) 29 | with _documents_tracer.span('stub.ListDocuments'): 30 | stub.ListDocuments(list_document_request) 31 | 32 | 33 | PROBE_FUNCTIONS = { 34 | 'documents': _documents, 35 | } 36 | -------------------------------------------------------------------------------- /cloudprober/grpc_gcp_prober/prober.py: -------------------------------------------------------------------------------- 1 | """Main method of the cloudprober as an entrypoint to execute probes.""" 2 | 3 | import os 4 | import argparse 5 | import sys 6 | import traceback 7 | import firestore_probes 8 | import grpc 9 | import grpc_gcp 10 | import pkg_resources 11 | import spanner_probes 12 | from stackdriver_util import StackdriverUtil 13 | from google import auth 14 | import google.auth.transport.grpc as transport_grpc 15 | from google.auth.transport.requests import Request 16 | from google.cloud.firestore_v1beta1.proto import firestore_pb2_grpc 17 | import google.protobuf.text_format 18 | from google.cloud.spanner_v1.proto import spanner_pb2_grpc 19 | 20 | _OAUTH_SCOPE = 'https://www.googleapis.com/auth/cloud-platform' 21 | 22 | _SPANNER_TARGET = os.environ['SPANNER_TARGET'] 23 | _FIRESTORE_TARGET = os.environ['FIRESTORE_TARGET'] 24 | 25 | 26 | def _get_args(): 27 | """Retrieves arguments passed in while executing the main method. 28 | 29 | Returns: 30 | An object containing all the values for each argument parsed in. 31 | 32 | Raises: 33 | NotImplementedError: An error occurred when api does not match any records. 34 | """ 35 | parser = argparse.ArgumentParser() 36 | parser.add_argument('--api', type=str, help='define the cloud api to probe') 37 | parser.add_argument('--extension', 38 | type=bool, 39 | help='options to use grpc-gcp extension') 40 | return parser.parse_args() 41 | 42 | 43 | def _secure_authorized_channel(credentials, 44 | request, 45 | target, 46 | ssl_credentials=None, 47 | **kwargs): 48 | metadata_plugin = transport_grpc.AuthMetadataPlugin(credentials, request) 49 | 50 | # Create a set of grpc.CallCredentials using the metadata plugin. 51 | google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin) 52 | 53 | if ssl_credentials is None: 54 | ssl_credentials = grpc.ssl_channel_credentials() 55 | 56 | # Combine the ssl credentials and the authorization credentials. 57 | composite_credentials = grpc.composite_channel_credentials( 58 | ssl_credentials, google_auth_credentials) 59 | 60 | return grpc_gcp.secure_channel(target, composite_credentials, **kwargs) 61 | 62 | 63 | def _get_stub_channel(target, use_extension=False): 64 | cred, _ = auth.default([_OAUTH_SCOPE]) 65 | if not use_extension: 66 | return _secure_authorized_channel(cred, Request(), target) 67 | config = grpc_gcp.api_config_from_text_pb( 68 | pkg_resources.resource_string(__name__, 'spanner.grpc.config')) 69 | options = [(grpc_gcp.API_CONFIG_CHANNEL_ARG, config)] 70 | return _secure_authorized_channel(cred, Request(), target, options=options) 71 | 72 | 73 | def _execute_probe(api, use_extension=False): 74 | """Execute a probe function given certain Cloud api and probe name. 75 | 76 | Args: 77 | api: the name of the api provider, e.g. "spanner", "firestore". 78 | use_extension: option to use grpc-gcp extension when creating channel. 79 | 80 | Raises: 81 | NotImplementedError: An error occurred when api does not match any records. 82 | """ 83 | util = StackdriverUtil(api) 84 | 85 | if api == 'spanner': 86 | channel = _get_stub_channel(_SPANNER_TARGET, use_extension) 87 | stub = spanner_pb2_grpc.SpannerStub(channel) 88 | probe_functions = spanner_probes.PROBE_FUNCTIONS 89 | elif api == 'firestore': 90 | channel = _get_stub_channel(_FIRESTORE_TARGET) 91 | stub = firestore_pb2_grpc.FirestoreStub(channel) 92 | probe_functions = firestore_probes.PROBE_FUNCTIONS 93 | else: 94 | raise NotImplementedError('gRPC prober is not implemented for %s !' % api) 95 | 96 | total = len(probe_functions) 97 | success = 0 98 | 99 | # Execute all probes for given api 100 | for probe_name in probe_functions: 101 | probe_function = probe_functions[probe_name] 102 | try: 103 | probe_function(stub) 104 | success += 1 105 | except Exception: # pylint: disable=broad-except 106 | # report any kind of exception to Stackdriver 107 | util.report_error(traceback.format_exc()) 108 | 109 | if success == total: 110 | util.set_success(True) 111 | 112 | # Summarize metrics 113 | util.output_metrics() 114 | 115 | # Fail this probe if any function fails 116 | if success != total: 117 | sys.exit(1) 118 | 119 | 120 | if __name__ == '__main__': 121 | args = _get_args() 122 | _execute_probe(args.api, args.extension) 123 | -------------------------------------------------------------------------------- /cloudprober/grpc_gcp_prober/spanner.grpc.config: -------------------------------------------------------------------------------- 1 | channel_pool: { 2 | max_size: 10 3 | max_concurrent_streams_low_watermark: 1 4 | } 5 | method: { 6 | name: "/google.spanner.v1.Spanner/CreateSession" 7 | affinity: { 8 | command: BIND 9 | affinity_key: "name" 10 | } 11 | } 12 | method: { 13 | name: "/google.spanner.v1.Spanner/GetSession" 14 | affinity: { 15 | command: BOUND 16 | affinity_key: "name" 17 | } 18 | } 19 | method: { 20 | name: "/google.spanner.v1.Spanner/DeleteSession" 21 | affinity: { 22 | command: UNBIND 23 | affinity_key: "name" 24 | } 25 | } 26 | method: { 27 | name: "/google.spanner.v1.Spanner/ExecuteSql" 28 | affinity: { 29 | command: BOUND 30 | affinity_key: "session" 31 | } 32 | } 33 | method: { 34 | name: "/google.spanner.v1.Spanner/ExecuteStreamingSql" 35 | affinity: { 36 | command: BOUND 37 | affinity_key: "session" 38 | } 39 | } 40 | method: { 41 | name: "/google.spanner.v1.Spanner/Read" 42 | affinity: { 43 | command: BOUND 44 | affinity_key: "session" 45 | } 46 | } 47 | method: { 48 | name: "/google.spanner.v1.Spanner/StreamingRead" 49 | affinity: { 50 | command: BOUND 51 | affinity_key: "session" 52 | } 53 | } 54 | method: { 55 | name: "/google.spanner.v1.Spanner/BeginTransaction" 56 | affinity: { 57 | command: BOUND 58 | affinity_key: "session" 59 | } 60 | } 61 | method: { 62 | name: "/google.spanner.v1.Spanner/Commit" 63 | affinity: { 64 | command: BOUND 65 | affinity_key: "session" 66 | } 67 | } 68 | method: { 69 | name: "/google.spanner.v1.Spanner/Rollback" 70 | affinity: { 71 | command: BOUND 72 | affinity_key: "session" 73 | } 74 | } 75 | method: { 76 | name: "/google.spanner.v1.Spanner/PartitionQuery" 77 | affinity: { 78 | command: BOUND 79 | affinity_key: "session" 80 | } 81 | } 82 | method: { 83 | name: "/google.spanner.v1.Spanner/PartitionRead" 84 | affinity: { 85 | command: BOUND 86 | affinity_key: "session" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /cloudprober/grpc_gcp_prober/stackdriver_util.py: -------------------------------------------------------------------------------- 1 | """Utilities for collecting metrics and errors to Stackdriver. 2 | """ 3 | 4 | import sys 5 | import versions 6 | from google.cloud import error_reporting 7 | 8 | 9 | class StackdriverUtil(object): 10 | """Utility class for collection Stackdriver metrics and errors. 11 | 12 | Use this class to format output so that cloudprober can scrape and create 13 | metrics based on these output. 14 | 15 | Attributes: 16 | api_name: The cloud api name of the metrics being generated. 17 | """ 18 | 19 | def __init__(self, api_name): 20 | self.metrics = {} 21 | self.success = False 22 | self.err_client = error_reporting.Client() 23 | self.api_name = api_name 24 | 25 | def add_metric(self, name, value): 26 | self.metrics[name] = value 27 | 28 | def add_metrics_dict(self, metrics_dict): 29 | self.metrics.update(metrics_dict) 30 | 31 | def set_success(self, success): 32 | self.success = success 33 | 34 | def output_metrics(self): 35 | """Format output before they can be made to Stackdriver metrics. 36 | 37 | Formatted output like 'metricvalue' will be scraped by Stackdriver as 38 | custom metrics. 39 | """ 40 | 41 | # output probe result. 42 | if self.success: 43 | sys.stdout.write('{}_success 1\n'.format(self.api_name)) 44 | else: 45 | sys.stdout.write('{}_success 0\n'.format(self.api_name)) 46 | 47 | # output other metrics. 48 | for key, value in self.metrics.iteritems(): 49 | sys.stdout.write('{} {}\n'.format(key, int(value))) 50 | 51 | def report_error(self, err): 52 | """Format error message to output to error reporting.""" 53 | # Write err log to Stackdriver logging 54 | sys.stderr.write(str(err) + '\n') 55 | # Report err to Stackdriver Error Reporting 56 | self.err_client.report( 57 | 'PythonProbeFailure: gRPC(v={}) fails on {} API. Details: {}\n'.format( 58 | versions.GRPC_VERSION, self.api_name, str(err))) 59 | -------------------------------------------------------------------------------- /cloudprober/grpc_gcp_prober/tracer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import opencensus.trace.tracer 4 | from opencensus.ext.stackdriver import trace_exporter as stackdriver_exporter 5 | from opencensus.common.transports.async_ import AsyncTransport 6 | 7 | 8 | def initialize_tracer(project_id=None): 9 | """ Initialize tracer 10 | 11 | Args: 12 | project_id: the project needed to be traced, default is fetched from environment variable $GOOGLE_CLOUD_PROJECT 13 | 14 | Raises: 15 | ValueError: An error occurred when project_id is neither passed in nor set as an environment variable 16 | """ 17 | 18 | if project_id is None: 19 | if os.environ.get('GOOGLE_CLOUD_PROJECT') is not None: 20 | project_id = os.environ['GOOGLE_CLOUD_PROJECT'] 21 | else: 22 | raise ValueError( 23 | 'Can not find a valid project_id to initialize the tracer, check if $GOOGLE_CLOUD_PROJECT is set') 24 | 25 | exporter = stackdriver_exporter.StackdriverExporter( 26 | project_id=project_id, 27 | transport=AsyncTransport # Use AsyncTransport to exclude exporting time 28 | ) 29 | tracer = opencensus.trace.tracer.Tracer( 30 | exporter=exporter 31 | ) 32 | return tracer 33 | -------------------------------------------------------------------------------- /cloudprober/grpc_gcp_prober/versions.py: -------------------------------------------------------------------------------- 1 | """This is a template file for holding version numbers.""" 2 | GRPC_VERSION = '1.11.0' 3 | -------------------------------------------------------------------------------- /cloudprober/requirements.txt: -------------------------------------------------------------------------------- 1 | grpcio 2 | grpcio-tools 3 | google-auth 4 | google-cloud-spanner 5 | google-cloud-firestore 6 | google-cloud-error-reporting 7 | opencensus==0.3.1 8 | opencensus-ext-stackdriver==0.1.1 9 | requests 10 | -------------------------------------------------------------------------------- /cloudprober/setup.py: -------------------------------------------------------------------------------- 1 | """Setup file for grpc-gcp-prober package. 2 | 3 | This specifies all the dependencies to install the module for python prober. 4 | """ 5 | 6 | import setuptools 7 | 8 | LICENSE = 'Apache License 2.0' 9 | 10 | CLASSIFIERS = [ 11 | 'Development Status :: 4 - Beta', 12 | 'Programming Language :: Python', 13 | 'Programming Language :: Python :: 2', 14 | 'Programming Language :: Python :: 2.7', 15 | 'Programming Language :: Python :: 3', 16 | 'Programming Language :: Python :: 3.4', 17 | 'Programming Language :: Python :: 3.5', 18 | 'Programming Language :: Python :: 3.6', 19 | 'License :: OSI Approved :: Apache Software License', 20 | ] 21 | 22 | setuptools.setup( 23 | name='grpc_gcp_prober', 24 | version='0.0.1', 25 | description='Prober scripts for cloud APIs in Python', 26 | author='Weiran Fang', 27 | author_email='weiranf@google.com', 28 | url='https://grpc.io', 29 | license=LICENSE, 30 | classifiers=CLASSIFIERS, 31 | packages=setuptools.find_packages(), 32 | include_package_data=True, 33 | install_requires=['grpcio', 'google-auth', 'requests'], 34 | ) 35 | -------------------------------------------------------------------------------- /cloudprober/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | 4 | # install grpc-gcp 5 | ../src/setup.sh 6 | 7 | # install grpc-gcp-prober 8 | cd "$(dirname "$0")" 9 | pip install --upgrade pip 10 | pip uninstall grpc-gcp-prober -y 11 | pip install -rrequirements.txt 12 | # ./codegen.sh 13 | pip install . 14 | -------------------------------------------------------------------------------- /cloudprober/template/__init__.py: -------------------------------------------------------------------------------- 1 | """Declare namespace packages.""" 2 | try: 3 | __import__('pkg_resources').declare_namespace(__name__) 4 | except ImportError: 5 | __path__ = __import__('pkgutil').extend_path(__path__, __name__) 6 | -------------------------------------------------------------------------------- /cloudprober/tests/firestore_probes_test.py: -------------------------------------------------------------------------------- 1 | """Tests for grpc_gcp_prober.firestore_probes.""" 2 | 3 | import unittest 4 | from grpc_gcp_prober import firestore_probes 5 | import mock 6 | 7 | 8 | class FirestoreProbesTest(unittest.TestCase): 9 | 10 | @mock.patch('google.firestore.v1beta1.firestore_pb2_grpc.FirestoreStub') 11 | def test_probe_documents(self, mock_stub_class): 12 | mock_stub = mock_stub_class.return_value 13 | test_metrics = {} 14 | firestore_probes._documents(mock_stub, test_metrics) 15 | mock_stub.ListDocuments.assert_called_once() 16 | self.assertGreater(len(test_metrics), 0) 17 | 18 | 19 | if __name__ == '__main__': 20 | unittest.main() 21 | -------------------------------------------------------------------------------- /cloudprober/tests/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | 5 | cp test_requirements.txt ../requirements.txt 6 | 7 | ../setup.sh 8 | 9 | python -m unittest discover -p '*_test.py' 10 | 11 | # Cleanup 12 | rm ./*.pyc 13 | rm ../requirements.txt 14 | rm -r ../google 15 | 16 | -------------------------------------------------------------------------------- /cloudprober/tests/spanner_probes_test.py: -------------------------------------------------------------------------------- 1 | """Tests for grpc_gcp_prober.spanner_probes.""" 2 | 3 | import unittest 4 | from grpc_gcp_prober import spanner_probes 5 | import mock 6 | from google.protobuf import empty_pb2 7 | from google.protobuf import struct_pb2 8 | from google.protobuf import timestamp_pb2 9 | from google.spanner.v1 import result_set_pb2 10 | from google.spanner.v1 import spanner_pb2 11 | from google.spanner.v1 import transaction_pb2 12 | 13 | 14 | class SpannerProbesTest(unittest.TestCase): 15 | 16 | @mock.patch('google.spanner.v1.spanner_pb2_grpc.SpannerStub') 17 | def test_session_management(self, mock_stub_class): 18 | mock_stub = mock_stub_class.return_value 19 | test_session = spanner_pb2.Session(name='test_session') 20 | mock_stub.CreateSession.return_value = test_session 21 | mock_stub.GetSession.return_value = test_session 22 | mock_stub.ListSessions.return_value = spanner_pb2.ListSessionsResponse( 23 | sessions=[test_session]) 24 | test_metrics = {} 25 | spanner_probes._session_management(mock_stub, test_metrics) 26 | mock_stub.CreateSession.assert_called_once() 27 | mock_stub.GetSession.assert_called_once() 28 | mock_stub.ListSessions.assert_called_once() 29 | mock_stub.DeleteSession.assert_called_once() 30 | self.assertGreater(len(test_metrics), 0) 31 | 32 | @mock.patch('google.spanner.v1.spanner_pb2_grpc.SpannerStub') 33 | def test_execute_sql(self, mock_stub_class): 34 | mock_stub = mock_stub_class.return_value 35 | test_session = spanner_pb2.Session(name='test_session') 36 | mock_stub.CreateSession.return_value = test_session 37 | mock_stub.ExecuteSql.return_value = result_set_pb2.ResultSet(rows=[ 38 | struct_pb2.ListValue( 39 | values=[struct_pb2.Value(string_value='test_username')]) 40 | ]) 41 | mock_stub.ExecuteStreamingSql.return_value = iter([ 42 | result_set_pb2.PartialResultSet( 43 | values=[struct_pb2.Value(string_value='test_username')]) 44 | ]) 45 | test_metrics = {} 46 | spanner_probes._execute_sql(mock_stub, test_metrics) 47 | mock_stub.CreateSession.assert_called_once() 48 | mock_stub.ExecuteSql.assert_called_once() 49 | mock_stub.ExecuteStreamingSql.assert_called_once() 50 | mock_stub.DeleteSession.assert_called_once() 51 | self.assertGreater(len(test_metrics), 0) 52 | 53 | @mock.patch('google.spanner.v1.spanner_pb2_grpc.SpannerStub') 54 | def test_read(self, mock_stub_class): 55 | mock_stub = mock_stub_class.return_value 56 | test_session = spanner_pb2.Session(name='test_session') 57 | mock_stub.CreateSession.return_value = test_session 58 | mock_stub.Read.return_value = result_set_pb2.ResultSet(rows=[ 59 | struct_pb2.ListValue( 60 | values=[struct_pb2.Value(string_value='test_username')]) 61 | ]) 62 | mock_stub.StreamingRead.return_value = iter([ 63 | result_set_pb2.PartialResultSet( 64 | values=[struct_pb2.Value(string_value='test_username')]) 65 | ]) 66 | test_metrics = {} 67 | spanner_probes._read(mock_stub, test_metrics) 68 | mock_stub.CreateSession.assert_called_once() 69 | mock_stub.Read.assert_called_once() 70 | mock_stub.StreamingRead.assert_called_once() 71 | mock_stub.DeleteSession.assert_called_once() 72 | self.assertGreater(len(test_metrics), 0) 73 | 74 | @mock.patch('google.spanner.v1.spanner_pb2_grpc.SpannerStub') 75 | def test_transaction(self, mock_stub_class): 76 | mock_stub = mock_stub_class.return_value 77 | test_session = spanner_pb2.Session(name='test_session') 78 | mock_stub.CreateSession.return_value = test_session 79 | mock_stub.BeginTransaction.return_value = \ 80 | transaction_pb2.Transaction(id='1') 81 | mock_stub.Commit.return_value = spanner_pb2.CommitResponse( 82 | commit_timestamp=timestamp_pb2.Timestamp(seconds=1)) 83 | mock_stub.RollbackRequest.return_value = empty_pb2.Empty() 84 | test_metrics = {} 85 | spanner_probes._transaction(mock_stub, test_metrics) 86 | mock_stub.CreateSession.assert_called_once() 87 | mock_stub.BeginTransaction.assert_called() 88 | mock_stub.Commit.assert_called_once() 89 | mock_stub.Rollback.assert_called_once() 90 | mock_stub.DeleteSession.assert_called_once() 91 | self.assertGreater(len(test_metrics), 0) 92 | 93 | @mock.patch('google.spanner.v1.spanner_pb2_grpc.SpannerStub') 94 | def test_partition(self, mock_stub_class): 95 | mock_stub = mock_stub_class.return_value 96 | test_session = spanner_pb2.Session(name='test_session') 97 | mock_stub.CreateSession.return_value = test_session 98 | test_partition_response = spanner_pb2.PartitionResponse( 99 | partitions=[spanner_pb2.Partition(partition_token='1')]) 100 | mock_stub.PartitionQuery.return_value = test_partition_response 101 | mock_stub.PartitionRead.return_value = test_partition_response 102 | test_metrics = {} 103 | spanner_probes._partition(mock_stub, test_metrics) 104 | mock_stub.CreateSession.assert_called_once() 105 | mock_stub.PartitionQuery.assert_called_once() 106 | mock_stub.PartitionRead.assert_called_once() 107 | mock_stub.DeleteSession.assert_called_once() 108 | self.assertGreater(len(test_metrics), 0) 109 | 110 | 111 | if __name__ == '__main__': 112 | unittest.main() 113 | -------------------------------------------------------------------------------- /cloudprober/tests/test_requirements.txt: -------------------------------------------------------------------------------- 1 | grpcio 2 | grpcio-tools 3 | google-auth 4 | google-cloud-error-reporting 5 | requests 6 | mock 7 | -------------------------------------------------------------------------------- /doc/cloudprober-guide.md: -------------------------------------------------------------------------------- 1 | # How To Add Python Probers for Cloud APIs 2 | 3 | The gRPC Cloudprober supports Python probers. Following steps 4 | shows how to add probes for a new Cloud API in Python. For this 5 | instruction, we take Firestore API as an example and walk through the process of 6 | adding Python probes for Firestore. 7 | 8 | ## Add Cloud API Probes 9 | 10 | The source code of the probes lives in [grpc_gcp_prober](../cloudprober/grpc_gcp_prober), 11 | you will need to modify this folder to add probes for new APIs. 12 | 13 | ### Implement new probes 14 | 15 | Create a new module named `firestore_probes.py` inside the source folder, and 16 | implement Python probes for the new cloud API. You can use `spanner_probes.py` 17 | as its prototype. For example, if you want to test the `ListDocuments` call 18 | from firestore stub: 19 | 20 | ```py 21 | def _documents(stub, util): 22 | """Probes to test ListDocuments grpc call from Firestore stub. 23 | 24 | Args: 25 | stub: An object of FirestoreStub. 26 | util: An object of StackdriverUtil. 27 | """ 28 | start = time.time() 29 | list_document_request = firestore_pb2.ListDocumentsRequest( 30 | parent=_PARENT_RESOURCE 31 | ) 32 | stub.ListDocuments(list_document_request) 33 | latency = (time.time() - start) * 1000 # calculate call latency in ms 34 | 35 | # use StackdriverUtil to collect custom metrics. 36 | util.add_metric('list_documents_latency_ms', latency) 37 | ``` 38 | 39 | Use a dict to map the probe name and the probe method. 40 | 41 | ```py 42 | PROBE_FUNCTIONS = { 43 | 'documents': _documents, 44 | } 45 | ``` 46 | 47 | Notice that `stub` and `util` objects are initialized in `prober.py`. We will 48 | discuss them in later sections. For complete code, check [firestore_probes.py](../cloudprober/grpc_gcp_prober/firestore_probes.py). 49 | 50 | ### Register new API stub 51 | 52 | Register the new cloud API in `prober.py`. `prober.py` is an entrypoint for all 53 | the probes of different cloud APIs. It creates the stub for the api and executes 54 | the probe functions defined for the specific cloud api. 55 | 56 | ```py 57 | from google.auth.transport.requests import Request 58 | from google.firestore.v1beta1 import firestore_pb2_grpc 59 | import firestore_probes 60 | 61 | _FIRESTORE_TARGET = 'firestore.googleapis.com' 62 | 63 | def _execute_probe(api, probe): 64 | # some other code ... 65 | 66 | if api == 'firestore': 67 | channel = secure_authorized_channel(cred, Request(), _FIRESTORE_TARGET) 68 | stub = firestore_pb2_grpc.FirestoreStub(channel) 69 | probe_functions = firestore_probes.PROBE_FUNCTIONS 70 | 71 | ... 72 | ``` 73 | 74 | ### Register probe in cloudprober 75 | 76 | Add the new probe you just implemented to `cloudprober.cfg`, so that when 77 | cloudprober is running, it will executes the probe and forward all metrics to 78 | Stackdriver. Use the template just like the other probes. 79 | 80 | ``` 81 | probe { 82 | type: EXTERNAL 83 | name: "firestore_documents" 84 | interval_msec: 300000 85 | timeout_msec: 30000 86 | targets { dummy_targets {} } # No targets for external probe 87 | external_probe { 88 | mode: ONCE 89 | command: "python -m grpc_gcp_prober.prober --api=firestore" 90 | } 91 | } 92 | ``` 93 | 94 | ## Stackdriver Mornitoring 95 | 96 | Use the [StackdriverUtil](../cloudprober/grpc_gcp_prober/stackdriver_util.py) 97 | to add custom metrics. 98 | 99 | ```py 100 | util = StackdriverUtil(api_name, probe_name) 101 | util.add_metric('metric_name', metric_value) 102 | ``` 103 | 104 | The StackdriverUtil will format the output (e.g. "read_latency_ms 100") so they 105 | can be scraped by cloudprober and then metrics will be automatically created and 106 | forwarded to Stackdriver as [Custom Metrics](https://cloud.google.com/monitoring/custom-metrics/). Later on, the metrics can be retrieved via [Metric Explore](https://app.google.stackdriver.com/metrics-explorer). 107 | The full name of the metric will be in the following format: 108 | 109 | ``` 110 | custom.googleapis.com/cloudprober/external// 111 | ``` 112 | 113 | ## Stackdriver Error Reporting 114 | [StackdriverUtil](../cloudprober/grpc_gcp_prober/stackdriver_util.py) also helps setting up 115 | [Error Reporting](https://cloud.google.com/error-reporting/docs/setup/python) 116 | to report any Error occurred during probes. In this way, if anything unusual 117 | occurs, it can be reported immediately. 118 | 119 | By default, all exceptions thrown by any probe will be reported to Error 120 | Reporting by StackdriverUtil. 121 | 122 | ## Alerting Notification 123 | 124 | There are two ways you can be notified for alerts: 125 | 126 | 1. Add [Alerting Policy](https://cloud.google.com/monitoring/alerts/) in 127 | Stackdriver Monitoring. And set up notification when certain metircs are absent 128 | or beyond/below a certain threshold. 129 | 130 | 2. Set up [Email Notification](https://cloud.google.com/error-reporting/docs/notifications) 131 | in Error Reporting. The alert will be triggered whenever an Error/Exception is 132 | reported by google-cloud-error-reporting client. Note that this option does not 133 | support email alias, you need to use the email that is associated with the 134 | Google Account and with necessary IAM roles. 135 | -------------------------------------------------------------------------------- /doc/gRPC-client-user-guide.md: -------------------------------------------------------------------------------- 1 | # Instructions for create a gRPC client for google cloud services 2 | 3 | ## Overview 4 | 5 | This instruction includes a step by step guide for creating a gRPC 6 | client to test the google cloud service from an empty linux 7 | VM, using GCE ubuntu 16.04 TLS instance. 8 | 9 | The main steps are followed as steps below: 10 | 11 | - Environment Prerequisites 12 | - Install gRPC-python, plugin and cloud API 13 | - Generate client API from .proto files 14 | - Create the client and send/receive RPC. 15 | 16 | ## Environment Prerequisite 17 | 18 | ## Install gRPC-python, plugin and cloud API 19 | ```sh 20 | $ [sudo] apt-get install python-pip 21 | # Install gRPC-python 22 | $ [sudo] pip install grpcio 23 | # Install gRPC-python plugin, which is used to generate pb files 24 | $ [sudo] pip install grpcio-tools 25 | # install google auth 26 | $ [sudo] pip install google-auth 27 | ``` 28 | 29 | ## Generate client API from .proto files 30 | The plugin is installed with [grpcio-tools](https://grpc.io/docs/tutorials/basic/python.html#generating-client-and-server-code). 31 | The command using plugin looks like 32 | ```sh 33 | $ mkdir project-python && cd project-python 34 | $ python -m grpc_tools.protoc --proto_path=/path/to/your/proto_path --python_out=./ \ 35 | --grpc_python_out=./ \ 36 | path/to/your/proto_dependency_directory1/*.proto \ 37 | path/to/your/proto_dependency_directory2/*.proto \ 38 | path/to/your/proto_service_directory/*.proto 39 | ``` 40 | 41 | Take firestore API inside [googleapis github repo](https://github.com/googleapis/googleapis) 42 | as example. The `proto` files needed are: 43 | ``` 44 | google/api/annotations.proto 45 | google/api/http.proto 46 | google/api/httpbody.proto 47 | google/longrunning/operations.proto 48 | google/rpc/code.proto 49 | google/rpc/error_details.proto 50 | google/rpc/status.proto google/type/latlng.proto 51 | google/firestore/v1beta1/firestore.proto 52 | google/firestore/v1beta1/common.proto 53 | google/firestore/v1beta1/query.proto 54 | google/firestore/v1beta1/write.proto 55 | google/firestore/v1beta1/document.proto. 56 | ``` 57 | The command generate client will be 58 | ``` 59 | python -m grpc_tools.protoc --proto_path=googleapis --python_out=./ --grpc_python_out=./ \ 60 | google/api/annotations.proto google/api/http.proto google/api/httpbody.proto \ 61 | google/longrunning/operations.proto google/rpc/code.proto google/rpc/error_details.proto \ 62 | google/rpc/status.proto google/type/latlng.proto google/firestore/v1beta1/firestore.proto \ 63 | google/firestore/v1beta1/common.proto google/firestore/v1beta1/query.proto \ 64 | google/firestore/v1beta1/write.proto google/firestore/v1beta1/document.proto 65 | ``` 66 | 67 | The client API library is generated under `project-python`. 68 | Take [`Firestore`](https://github.com/googleapis/googleapis/blob/master/google/firestore/v1beta1/firestore.proto) 69 | as example, the Client API is under 70 | `project-python/google/firestore/v1beta1` depends on your 71 | package namespace inside .proto file. An easy way to find your client is 72 | ```sh 73 | $ cd project-python 74 | $ find ./ -name [service_name: eg, firestore, cluster_service]* 75 | ``` 76 | 77 | ## Create the client and send/receive RPC. 78 | Now it's time to use the client API to send and receive RPCs. 79 | 80 | **Set credentials file** 81 | 82 | This is important otherwise your RPC response will be a permission error. 83 | ``` sh 84 | $ vim $HOME/key.json 85 | ## Paste you credential file downloaded from your cloud project 86 | ## which you can find in APIs&Services => credentials => create credentials 87 | ## => Service account key => your credentials 88 | $ export GOOGLE_APPLICATION_CREDENTIALS=$HOME/key.json 89 | ``` 90 | 91 | **Implement Service Client** 92 | 93 | Take a unary-unary RPC `listDocument` from `FirestoreClient` as example. 94 | Create a file name `list_document_client.py`. 95 | - Create `__init__.py`. You need to create `__init__.py` to let your python 96 | script find the module. 97 | ``` 98 | $ vim google/__init__.py 99 | $ vim google/firestore/__init__.py 100 | $ vim google/firestore/v1beta1/__init__.py 101 | $ vim google/firestore/rpc/__init__.py 102 | $ vim google/firestore/api/__init__.py 103 | $ vim google/firestore/type/__init__.py 104 | $ vim google/firestore/longrunning/__init__.py 105 | ``` 106 | - Import library 107 | ``` 108 | from google import auth as google_auth 109 | from google.auth.transport import requests as google_auth_transport_requests 110 | from google.auth.transport import grpc as google_auth_transport_grpc 111 | from google.firestore.v1beta1 import firestore_pb2 112 | from google.firestore.v1beta1 import firestore_pb2_grpc 113 | ``` 114 | - Set Google Auth. Please see the referece for 115 | [authenticate with Google using an Oauth2 token](https://grpc.io/docs/guides/auth.html#python) 116 | ``` 117 | scoped_credentials, _ = google_auth.default(scopes=('https://www.googleapis.com/auth/datastore',)) 118 | request = google_auth_transport_requests.Request() 119 | channel = google_auth_transport_grpc.secure_authorized_channel( 120 | scoped_credentials, request, 'firestore.googleapis.com:443') 121 | ``` 122 | There is an optional way to set the key.json without export 123 | `GOOGLE_APPLICATION_CREDENTIALS` 124 | ``` 125 | credentials = service_account.Credentials.from_service_account_file("/path/to/key.json") 126 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 127 | request = google_auth_transport_requests.Request() 128 | channel = google_auth_transport_grpc.secure_authorized_channel( 129 | scoped_credentials, request, 'firestore.googleapis.com:443') 130 | ``` 131 | 132 | - Create Stub 133 | ``` 134 | stub = firestore_pb2_grpc.FirestoreStub(channel) 135 | ``` 136 | - Invoke RPC 137 | ``` 138 | list_document_request = firestore_pb2.ListDocumentsRequest( 139 | parent = 'projects/xxxxx/databases/(default)/documents') 140 | list_document_response = stub.ListDocuments(list_document_request) 141 | ``` 142 | - Print RPC response 143 | ``` 144 | print(list_document_response) 145 | ``` 146 | - Run the script 147 | ```sh 148 | $ python list_document_client.py 149 | ``` 150 | 151 | For different kinds of RPC(unary-unary, unary-stream, stream-unary, stream-stream), 152 | please check [grpc.io Python part](https://grpc.io/docs/tutorials/basic/python.html#simple-rpc) 153 | for reference. 154 | -------------------------------------------------------------------------------- /firestore/examples/end2end/doc/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/grpc-gcp-python/5a2cd9807bbaf1b85402a2a364775e5b65853df6/firestore/examples/end2end/doc/.gitignore -------------------------------------------------------------------------------- /firestore/examples/end2end/src/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/grpc-gcp-python/5a2cd9807bbaf1b85402a2a364775e5b65853df6/firestore/examples/end2end/src/.gitignore -------------------------------------------------------------------------------- /firestore/examples/end2end/src/BatchGetDocuments.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | def main(): 27 | 28 | subprocess.call('clear') 29 | 30 | fl = os.path.dirname(os.path.abspath(__file__)) 31 | fn = os.path.join(fl, 'grpc.json') 32 | 33 | with open(fn) as grpc_file: 34 | 35 | item = json.load(grpc_file) 36 | 37 | creds = item["grpc"]["BatchGetDocuments"]["credentials"] 38 | 39 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 40 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 41 | http_request = google.auth.transport.requests.Request() 42 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 43 | 44 | stub = firestore_pb2_grpc.FirestoreStub(channel) 45 | 46 | # database and documents defined in the grpc.json file 47 | database = item["grpc"]["BatchGetDocuments"]["database"] 48 | documents = item["grpc"]["BatchGetDocuments"]["documents"] 49 | 50 | if documents == ' ': 51 | 52 | print("Please enter the document Id's you would like to retrieve, e.g.\n") 53 | print("projects/geoff-python/databases/(default)/documents/users/alovelace \n") 54 | 55 | documents = raw_input(":") 56 | 57 | batch_get_document_request = firestore_pb2.BatchGetDocumentsRequest(database = database, documents = [documents]) 58 | batch_get_document_response = stub.BatchGetDocuments(batch_get_document_request) 59 | 60 | print('staring read from batch: ', type(batch_get_document_response)) 61 | for get_document in batch_get_document_response: 62 | print(get_document) 63 | 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/BeginTransaction.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | 27 | def main(): 28 | 29 | subprocess.call('clear') 30 | 31 | fl = os.path.dirname(os.path.abspath(__file__)) 32 | fn = os.path.join(fl, 'grpc.json') 33 | 34 | with open(fn) as grpc_file: 35 | 36 | item = json.load(grpc_file) 37 | 38 | # credentials file location defined in grpc.json file 39 | creds = item["grpc"]["BeginTransaction"]["credentials"] 40 | 41 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 42 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 43 | http_request = google.auth.transport.requests.Request() 44 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 45 | 46 | stub = firestore_pb2_grpc.FirestoreStub(channel) 47 | 48 | now = time.time() 49 | seconds = int(now) 50 | timestamp = timestamp_pb2.Timestamp(seconds=seconds) 51 | 52 | # database definded in grpc.json file 53 | database = item["grpc"]["BeginTransaction"]["database"] 54 | 55 | options = common_pb2.TransactionOptions(read_only = common_pb2.TransactionOptions.ReadOnly(read_time = timestamp)) 56 | 57 | begin_transaction_request = firestore_pb2.BeginTransactionRequest(database = database, options = options) 58 | 59 | begin_transaction_response = stub.BeginTransaction(begin_transaction_request) 60 | 61 | print(begin_transaction_response) 62 | 63 | 64 | if __name__ == "__main__": 65 | main() 66 | 67 | 68 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/Commit.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | from google.firestore.admin.v1beta1 import index_pb2 26 | from google.firestore.admin.v1beta1 import index_pb2_grpc 27 | from google.firestore.admin.v1beta1 import firestore_admin_pb2 28 | from google.firestore.admin.v1beta1 import firestore_admin_pb2_grpc 29 | 30 | 31 | 32 | def main(): 33 | 34 | subprocess.call('clear') 35 | 36 | fl = os.path.dirname(os.path.abspath(__file__)) 37 | fn = os.path.join(fl, 'grpc.json') 38 | 39 | with open(fn) as grpc_file: 40 | 41 | item = json.load(grpc_file) 42 | 43 | creds = item["grpc"]["Commit"]["credentials"] 44 | 45 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 46 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 47 | http_request = google.auth.transport.requests.Request() 48 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 49 | 50 | stub = firestore_pb2_grpc.FirestoreStub(channel) 51 | 52 | now = time.time() 53 | seconds = int(now) 54 | timestamp = timestamp_pb2.Timestamp(seconds=seconds) 55 | 56 | # database defined in the grpc.json file 57 | database = item["grpc"]["Commit"]["database"] 58 | 59 | options = common_pb2.TransactionOptions(read_write = common_pb2.TransactionOptions.ReadWrite()) 60 | begin_transaction_request = firestore_pb2.BeginTransactionRequest(database = database, options = options) 61 | begin_transaction_response = stub.BeginTransaction(begin_transaction_request) 62 | transaction = begin_transaction_response.transaction 63 | 64 | stub = firestore_pb2_grpc.FirestoreStub(channel) 65 | 66 | now = time.time() 67 | seconds = int(now) 68 | timestamp = timestamp_pb2.Timestamp(seconds=seconds) 69 | 70 | field_paths = {} 71 | # document mask field_path is defined in the grpc.json file 72 | field_paths= item["grpc"]["Commit"]["field_paths"] 73 | update_mask = common_pb2.DocumentMask(field_paths = [field_paths]) 74 | 75 | # document_fileds is defined in the grpc.json file 76 | fields=item["grpc"]["Commit"]["fields"] 77 | 78 | # document_name is defined in the grpc.json file 79 | name =item["grpc"]["Commit"]["name"] 80 | 81 | update = document_pb2.Document(name=name, fields=fields, create_time = timestamp, update_time = timestamp) 82 | 83 | writes = {} 84 | database = item["grpc"]["Commit"]["database"] 85 | writes = write_pb2.Write(update_mask = update_mask, update=update) 86 | 87 | commit_request = firestore_pb2.CommitRequest(database = database, writes = [writes], transaction = transaction ) 88 | commit_response = stub.Commit(commit_request) 89 | 90 | print(commit_response) 91 | 92 | 93 | if __name__ == "__main__": 94 | main() 95 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/CreateDocument.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | def main(): 27 | 28 | subprocess.call('clear') 29 | 30 | fl = os.path.dirname(os.path.abspath(__file__)) 31 | fn = os.path.join(fl, 'grpc.json') 32 | 33 | with open(fn) as grpc_file: 34 | 35 | item = json.load(grpc_file) 36 | 37 | creds = item["grpc"]["CreateDocument"]["credentials"] 38 | 39 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 40 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 41 | http_request = google.auth.transport.requests.Request() 42 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 43 | 44 | stub = firestore_pb2_grpc.FirestoreStub(channel) 45 | 46 | # parent defined in the grpc.json file 47 | parent = item["grpc"]["CreateDocument"]["parent"] 48 | 49 | # collection_id defined in the grpc.json 50 | collection_id = item["grpc"]["CreateDocument"]["collection_id"] 51 | 52 | document_id = item["grpc"]["CreateDocument"]["document_id"] 53 | 54 | if document_id == ' ': 55 | 56 | document_id = raw_input("Please provde a document_id: \n") 57 | 58 | document = {} 59 | 60 | create_document_request = firestore_pb2.CreateDocumentRequest(parent=parent,collection_id=collection_id, document_id=document_id, document=document) 61 | document = stub.CreateDocument(create_document_request) 62 | print(document) 63 | 64 | 65 | if __name__ == "__main__": 66 | main() 67 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/CreateIndex.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.admin.v1beta1 import index_pb2 15 | from google.firestore.admin.v1beta1 import index_pb2_grpc 16 | from google.firestore.admin.v1beta1 import firestore_admin_pb2 17 | from google.firestore.admin.v1beta1 import firestore_admin_pb2_grpc 18 | 19 | def main(): 20 | 21 | subprocess.call('clear') 22 | 23 | fl = os.path.dirname(os.path.abspath(__file__)) 24 | fn = os.path.join(fl, 'grpc.json') 25 | 26 | with open(fn) as grpc_file: 27 | 28 | item = json.load(grpc_file) 29 | 30 | creds = item["grpc"]["CreateIndex"]["credentials"] 31 | 32 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 33 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 34 | http_request = google.auth.transport.requests.Request() 35 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 36 | 37 | 38 | stub = firestore_admin_pb2_grpc.FirestoreAdminStub(channel) 39 | 40 | # name, parent and collection_id are definded in the grpc.json file 41 | name = item["grpc"]["CreateIndex"]["name"] 42 | parent = item["grpc"]["CreateIndex"]["parent"] 43 | collection_id = item["grpc"]["CreateIndex"]["collection_id"] 44 | 45 | fields = [] 46 | 47 | # field_path1 and mode1 are defined in the grpc.json file 48 | field_path1= item["grpc"]["CreateIndex"]["field_path1"] 49 | mode1= item["grpc"]["CreateIndex"]["mode1"] 50 | 51 | fields1 = index_pb2.IndexField(field_path=field_path1, mode=mode1) 52 | 53 | 54 | # field_path2 and mode2 are defined in the grpc.json file 55 | field_path2= item["grpc"]["CreateIndex"]["field_path2"] 56 | mode2= item["grpc"]["CreateIndex"]["mode2"] 57 | 58 | fields2 = index_pb2.IndexField(field_path=field_path2, mode=mode2) 59 | 60 | fields = [fields1, fields2] 61 | 62 | index = index_pb2.Index(name=name, collection_id=collection_id, fields=fields) 63 | 64 | create_index_request = firestore_admin_pb2.CreateIndexRequest(parent=parent, index=index) 65 | create_index_response = stub.CreateIndex(create_index_request) 66 | 67 | print(create_index_response) 68 | 69 | if __name__ == "__main__": 70 | main() 71 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/DeleteDocument.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | def main(): 27 | 28 | 29 | fl = os.path.dirname(os.path.abspath(__file__)) 30 | fn = os.path.join(fl, 'grpc.json') 31 | 32 | with open(fn) as grpc_file: 33 | 34 | subprocess.call('clear') 35 | 36 | item = json.load(grpc_file) 37 | 38 | creds = item["grpc"]["DeleteDocuments"]["credentials"] 39 | 40 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 41 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 42 | http_request = google.auth.transport.requests.Request() 43 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 44 | 45 | # name is defined in the grpc.json file 46 | name = item["grpc"]["DeleteDocuments"]["name"] 47 | 48 | if name == ' ' : 49 | 50 | name = raw_input("Please enter the resource name of the document to delete, e.g. project/{project_id}/databases/{database_id}/documents/{document_path}: \n") 51 | 52 | stub = firestore_pb2_grpc.FirestoreStub(channel) 53 | 54 | delete_document_request = firestore_pb2.DeleteDocumentRequest(name = name) 55 | stub.DeleteDocument(delete_document_request) 56 | 57 | 58 | if __name__ == "__main__": 59 | main() 60 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/DeleteIndex.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.admin.v1beta1 import index_pb2 15 | from google.firestore.admin.v1beta1 import index_pb2_grpc 16 | from google.firestore.admin.v1beta1 import firestore_admin_pb2 17 | from google.firestore.admin.v1beta1 import firestore_admin_pb2_grpc 18 | 19 | def main(): 20 | 21 | subprocess.call('clear') 22 | 23 | fl = os.path.dirname(os.path.abspath(__file__)) 24 | fn = os.path.join(fl, 'grpc.json') 25 | 26 | with open(fn) as grpc_file: 27 | 28 | item = json.load(grpc_file) 29 | 30 | creds = item["grpc"]["DeleteIndex"]["credentials"] 31 | 32 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 33 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 34 | http_request = google.auth.transport.requests.Request() 35 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 36 | 37 | 38 | stub = firestore_admin_pb2_grpc.FirestoreAdminStub(channel) 39 | 40 | # parent definded in the grpc.json file 41 | parent = item["grpc"]["DeleteIndex"]["parent"] 42 | 43 | list_indexes_request = firestore_admin_pb2.ListIndexesRequest(parent=parent) 44 | list_indexes_response = {} 45 | list_indexes_response = stub.ListIndexes(list_indexes_request) 46 | 47 | print("\n") 48 | for index in list_indexes_response.indexes: 49 | print(index.name) 50 | 51 | print("\n") 52 | 53 | name = raw_input("Please enter the index to delete: \n") 54 | 55 | stub = firestore_admin_pb2_grpc.FirestoreAdminStub(channel) 56 | 57 | delete_index_request = firestore_admin_pb2.DeleteIndexRequest(name=name) 58 | stub.DeleteIndex(delete_index_request) 59 | 60 | 61 | if __name__ =="__main__": 62 | main() 63 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/GetDocument.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | def main(): 27 | 28 | subprocess.call('clear') 29 | 30 | fl = os.path.dirname(os.path.abspath(__file__)) 31 | fn = os.path.join(fl, 'grpc.json') 32 | 33 | with open(fn) as grpc_file: 34 | 35 | item = json.load(grpc_file) 36 | 37 | creds = item["grpc"]["GetDocument"]["credentials"] 38 | 39 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 40 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 41 | http_request = google.auth.transport.requests.Request() 42 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 43 | 44 | stub = firestore_pb2_grpc.FirestoreStub(channel) 45 | 46 | now = time.time() 47 | seconds = int(now) 48 | timestamp = timestamp_pb2.Timestamp(seconds=seconds) 49 | 50 | field_paths = {} 51 | 52 | # field_paths is set in the grpc.json file 53 | field_paths=item["grpc"]["GetDocument"]["document_mask_field_path"] 54 | 55 | mask = common_pb2.DocumentMask(field_paths = [field_paths]) 56 | 57 | 58 | # name is set in the grpc.json file 59 | name = item["grpc"]["GetDocument"]["name"] 60 | 61 | if name == ' ' : 62 | 63 | name = raw_input("Please enter the resource name of the Document to get, e.g. projects/{project_id}/databases/{database_id}/documents/{document_path}: \n") 64 | 65 | 66 | get_document_request = firestore_pb2.GetDocumentRequest(name=name, mask=mask) 67 | get_document_response = stub.GetDocument(get_document_request) 68 | 69 | print(get_document_response) 70 | 71 | if __name__ == "__main__": 72 | main() 73 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/GetIndex.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | #from google.firestore.v1beta1 import firestore_pb2 15 | #from google.firestore.v1beta1 import firestore_pb2_grpc 16 | #from google.firestore.v1beta1 import document_pb2 17 | #from google.firestore.v1beta1 import document_pb2_grpc 18 | #from google.firestore.v1beta1 import common_pb2 19 | #from google.firestore.v1beta1 import common_pb2_grpc 20 | #from google.firestore.v1beta1 import write_pb2 21 | #from google.firestore.v1beta1 import write_pb2_grpc 22 | #from google.protobuf import empty_pb2 23 | #from google.protobuf import timestamp_pb2 24 | 25 | from google.firestore.admin.v1beta1 import index_pb2 26 | from google.firestore.admin.v1beta1 import index_pb2_grpc 27 | from google.firestore.admin.v1beta1 import firestore_admin_pb2 28 | from google.firestore.admin.v1beta1 import firestore_admin_pb2_grpc 29 | 30 | def main(): 31 | 32 | subprocess.call('clear') 33 | 34 | fl = os.path.dirname(os.path.abspath(__file__)) 35 | fn = os.path.join(fl, 'grpc.json') 36 | 37 | with open(fn) as grpc_file: 38 | 39 | item = json.load(grpc_file) 40 | 41 | creds = item["grpc"]["GetIndex"]["credentials"] 42 | 43 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 44 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 45 | http_request = google.auth.transport.requests.Request() 46 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 47 | 48 | 49 | stub = firestore_admin_pb2_grpc.FirestoreAdminStub(channel) 50 | 51 | parent = item["grpc"]["GetIndex"]["parent"] 52 | 53 | list_indexes_request = firestore_admin_pb2.ListIndexesRequest(parent=parent) 54 | list_indexes_response = {} 55 | list_indexes_response = stub.ListIndexes(list_indexes_request) 56 | 57 | print("\n") 58 | for index in list_indexes_response.indexes: 59 | print(index) 60 | 61 | print("\n") 62 | 63 | name = raw_input("Please enter the index to get: \n") 64 | 65 | 66 | stub = firestore_admin_pb2_grpc.FirestoreAdminStub(channel) 67 | 68 | get_index_request = firestore_admin_pb2.GetIndexRequest(name=name) 69 | get_index_response = stub.GetIndex(get_index_request) 70 | 71 | print(get_index_response) 72 | 73 | 74 | if __name__ == "__main__": 75 | main() 76 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/ListCollectionIds.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | def main(): 27 | 28 | subprocess.call('clear') 29 | 30 | fl = os.path.dirname(os.path.abspath(__file__)) 31 | fn = os.path.join(fl, 'grpc.json') 32 | 33 | with open(fn) as grpc_file: 34 | 35 | item = json.load(grpc_file) 36 | 37 | creds = item["grpc"]["ListCollectionIds"]["credentials"] 38 | 39 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 40 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 41 | http_request = google.auth.transport.requests.Request() 42 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 43 | 44 | stub = firestore_pb2_grpc.FirestoreStub(channel) 45 | 46 | parent = item["grpc"]["ListCollectionIds"]["parent"] 47 | list_collectionIds_request = firestore_pb2.ListCollectionIdsRequest(parent = parent) 48 | list_collectionIds_response = stub.ListCollectionIds(list_collectionIds_request) 49 | print(list_collectionIds_response) 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/ListDocuments.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | def main(): 27 | 28 | 29 | fl = os.path.dirname(os.path.abspath(__file__)) 30 | fn = os.path.join(fl, 'grpc.json') 31 | 32 | with open(fn) as grpc_file: 33 | 34 | item = json.load(grpc_file) 35 | 36 | creds = item["grpc"]["ListDocuments"]["credentials"] 37 | 38 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 39 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 40 | http_request = google.auth.transport.requests.Request() 41 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 42 | 43 | stub = firestore_pb2_grpc.FirestoreStub(channel) 44 | 45 | parent = item["grpc"]["ListDocuments"]["parent"] 46 | 47 | list_document_request = firestore_pb2.ListDocumentsRequest(parent = 'projects/geoff-python/databases/(default)/documents') 48 | list_document_response = stub.ListDocuments(list_document_request) 49 | print(list_document_response) 50 | 51 | if __name__ == "__main__": 52 | main() 53 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/ListIndex.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.admin.v1beta1 import index_pb2 15 | from google.firestore.admin.v1beta1 import index_pb2_grpc 16 | from google.firestore.admin.v1beta1 import firestore_admin_pb2 17 | from google.firestore.admin.v1beta1 import firestore_admin_pb2_grpc 18 | 19 | def main(): 20 | 21 | 22 | fl = os.path.dirname(os.path.abspath(__file__)) 23 | fn = os.path.join(fl, 'grpc.json') 24 | 25 | with open(fn) as grpc_file: 26 | 27 | item = json.load(grpc_file) 28 | 29 | creds = item["grpc"]["ListIndex"]["credentials"] 30 | 31 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 32 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 33 | http_request = google.auth.transport.requests.Request() 34 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 35 | 36 | 37 | stub = firestore_admin_pb2_grpc.FirestoreAdminStub(channel) 38 | 39 | parent = item["grpc"]["ListIndex"]["parent"] 40 | 41 | list_indexes_request = firestore_admin_pb2.ListIndexesRequest(parent=parent) 42 | list_indexes_response = {} 43 | list_indexes_response = stub.ListIndexes(list_indexes_request) 44 | 45 | print("\n") 46 | for index in list_indexes_response.indexes: 47 | print(index) 48 | 49 | print("\n") 50 | 51 | if __name__ == "__main__": 52 | main() 53 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/README: -------------------------------------------------------------------------------- 1 | 1) Firestore GRPC are documented here https://cloud.google.com/firestore/docs/reference/rpc/google.firestore.v1beta1. 2 | 3 | 2) GRPC parameters are defined in the grpc.json file. 4 | 5 | 3) If a parameter is missing from the grpc.json file the end user will be prompted for the parameter input for GRPC's that accept interactive input. 6 | 7 | 4) The console program can be invoked at the command prompt: 8 | 9 | ./grpcCTL 10 | 11 | 5) Each GRPC can be run individually, for example: 12 | 13 | ./ListDocuments.py 14 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/Rollback.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | def main(): 27 | 28 | 29 | fl = os.path.dirname(os.path.abspath(__file__)) 30 | fn = os.path.join(fl, 'grpc.json') 31 | 32 | with open(fn) as grpc_file: 33 | 34 | item = json.load(grpc_file) 35 | 36 | creds = item["grpc"]["Rollback"]["credentials"] 37 | 38 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 39 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 40 | http_request = google.auth.transport.requests.Request() 41 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 42 | 43 | 44 | stub = firestore_pb2_grpc.FirestoreStub(channel) 45 | 46 | database = item["grpc"]["Rollback"]["database"] 47 | 48 | begin_transaction_request = firestore_pb2.BeginTransactionRequest(database = database) 49 | begin_transaction_response = stub.BeginTransaction(begin_transaction_request) 50 | 51 | rollback_request = firestore_pb2.RollbackRequest(database=database, transaction=begin_transaction_response.transaction) 52 | rollback_response = stub.Rollback(rollback_request) 53 | print(rollback_response) 54 | 55 | 56 | if __name__ == "__main__": 57 | main() 58 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/RunQuery.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.firestore.v1beta1 import query_pb2 23 | from google.firestore.v1beta1 import query_pb2_grpc 24 | 25 | from google.protobuf import empty_pb2 26 | from google.protobuf import timestamp_pb2 27 | from google.protobuf import wrappers_pb2 28 | 29 | 30 | 31 | def main(): 32 | 33 | fl = os.path.dirname(os.path.abspath(__file__)) 34 | fn = os.path.join(fl, 'grpc.json') 35 | 36 | with open(fn) as grpc_file: 37 | 38 | item = json.load(grpc_file) 39 | 40 | creds = item["grpc"]["RunQuery"]["credentials"] 41 | 42 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 43 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 44 | http_request = google.auth.transport.requests.Request() 45 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 46 | 47 | stub = firestore_pb2_grpc.FirestoreStub(channel) 48 | 49 | 50 | parent = item["grpc"]["RunQuery"]["parent"] 51 | field_path = item["grpc"]["RunQuery"]["field_path"] 52 | 53 | fields = {} 54 | fields = query_pb2.StructuredQuery.FieldReference(field_path = field_path) 55 | select = query_pb2.StructuredQuery.Projection(fields = [fields]) 56 | 57 | structured_query = query_pb2.StructuredQuery(select=select) 58 | 59 | run_query_request = firestore_pb2.RunQueryRequest(parent=parent, structured_query=structured_query) 60 | run_query_response = stub.RunQuery(run_query_request) 61 | 62 | print('starting read from batch: ', type(run_query_response)) 63 | for runquery in run_query_response: 64 | print(runquery) 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/UpdateDocument.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | def main(): 27 | 28 | 29 | fl = os.path.dirname(os.path.abspath(__file__)) 30 | fn = os.path.join(fl, 'grpc.json') 31 | 32 | with open(fn) as grpc_file: 33 | 34 | item = json.load(grpc_file) 35 | 36 | creds = item["grpc"]["UpdateDocument"]["credentials"] 37 | 38 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 39 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 40 | http_request = google.auth.transport.requests.Request() 41 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 42 | 43 | 44 | stub = firestore_pb2_grpc.FirestoreStub(channel) 45 | 46 | now = time.time() 47 | seconds = int(now) 48 | timestamp = timestamp_pb2.Timestamp(seconds=seconds) 49 | 50 | 51 | # name is set in the grpc.json file 52 | name = item["grpc"]["UpdateDocument"]["name"] 53 | 54 | if name == ' ': 55 | name=raw_input("Please provide the resource name of the document to be updated: \n") 56 | 57 | field=raw_input('Please provide the field to be updated, e.g. "foo" ') 58 | current_value=raw_input('Please provide the current value of the field to be updated, e.g. "bar" ') 59 | 60 | update_mask = common_pb2.DocumentMask(field_paths = [field, current_value]) 61 | 62 | value=raw_input("Please provide the new value of the field to update using the following syntax, e.g. 'foo_boo' \n") 63 | 64 | value_ = document_pb2.Value(string_value = value) 65 | 66 | document = document_pb2.Document(name=name, fields={field:value_}) 67 | 68 | update_document_request = firestore_pb2.UpdateDocumentRequest(document=document, update_mask=common_pb2.DocumentMask(field_paths = [field])) 69 | update_document_response = stub.UpdateDocument(update_document_request) 70 | 71 | print(update_document_response) 72 | 73 | if __name__ == "__main__": 74 | main() 75 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/Write.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | import sys 3 | import os 4 | import json 5 | import grpc 6 | import time 7 | import subprocess 8 | 9 | from google.oauth2 import service_account 10 | import google.oauth2.credentials 11 | import google.auth.transport.requests 12 | import google.auth.transport.grpc 13 | 14 | from google.firestore.v1beta1 import firestore_pb2 15 | from google.firestore.v1beta1 import firestore_pb2_grpc 16 | from google.firestore.v1beta1 import document_pb2 17 | from google.firestore.v1beta1 import document_pb2_grpc 18 | from google.firestore.v1beta1 import common_pb2 19 | from google.firestore.v1beta1 import common_pb2_grpc 20 | from google.firestore.v1beta1 import write_pb2 21 | from google.firestore.v1beta1 import write_pb2_grpc 22 | from google.protobuf import empty_pb2 23 | from google.protobuf import timestamp_pb2 24 | 25 | 26 | def first_message(database, write): 27 | messages = [ 28 | firestore_pb2.WriteRequest(database = database, writes = []) 29 | ] 30 | for msg in messages: 31 | yield msg 32 | 33 | def generate_messages(database, writes, stream_id, stream_token): 34 | # writes can be an array and append to the messages, so it can write multiple Write 35 | # here just write one as example 36 | messages = [ 37 | firestore_pb2.WriteRequest(database=database, writes = []), 38 | firestore_pb2.WriteRequest(database=database, writes = [writes], stream_id = stream_id, stream_token = stream_token) 39 | ] 40 | for msg in messages: 41 | yield msg 42 | 43 | 44 | 45 | 46 | def main(): 47 | 48 | 49 | fl = os.path.dirname(os.path.abspath(__file__)) 50 | fn = os.path.join(fl, 'grpc.json') 51 | 52 | with open(fn) as grpc_file: 53 | 54 | item = json.load(grpc_file) 55 | 56 | creds = item["grpc"]["Write"]["credentials"] 57 | 58 | credentials = service_account.Credentials.from_service_account_file("{}".format(creds)) 59 | scoped_credentials = credentials.with_scopes(['https://www.googleapis.com/auth/datastore']) 60 | http_request = google.auth.transport.requests.Request() 61 | channel = google.auth.transport.grpc.secure_authorized_channel(scoped_credentials, http_request, 'firestore.googleapis.com:443') 62 | 63 | stub = firestore_pb2_grpc.FirestoreStub(channel) 64 | 65 | database = item["grpc"]["Write"]["database"] 66 | name = item["grpc"]["Write"]["name"] 67 | first_write = write_pb2.Write() 68 | 69 | responses = stub.Write(first_message(database, first_write)) 70 | for response in responses: 71 | print("Received message %s" % (response.stream_id)) 72 | print(response.stream_token) 73 | 74 | value_ = document_pb2.Value(string_value = "foo_boo") 75 | update = document_pb2.Document(name=name, fields={"foo":value_}) 76 | writes = write_pb2.Write(update_mask=common_pb2.DocumentMask(field_paths = ["foo"]), update=update) 77 | r2 = stub.Write(generate_messages(database, writes, response.stream_id, response.stream_token)) 78 | for r in r2: 79 | print(r.write_results) 80 | 81 | if __name__ == "__main__": 82 | main() 83 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/grpc.json: -------------------------------------------------------------------------------- 1 | { 2 | "grpc": { 3 | "BatchGetDocuments": { 4 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 5 | "database": "projects/geoff-python/databases/(default)", 6 | "documents": "projects/geoff-python/databases/(default)/documents/users/aturing" 7 | }, 8 | "BeginTransaction": { 9 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 10 | "database": "projects/geoff-python/databases/(default)" 11 | }, 12 | "Commit": { 13 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 14 | "database": "projects/geoff-python/databases/(default)", 15 | "field_paths": "aturing.born", 16 | "fields": "aturing.born", 17 | "name": "projects/geoff-python/databases/(default)/documents/users/aturing" 18 | }, 19 | "CreateDocument": { 20 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 21 | "parent": "projects/geoff-python/databases/(default)/documents/test_document/my_chatroom", 22 | "collection_id": "documentId506676927", 23 | "document_id": " " 24 | }, 25 | "DeleteDocuments": { 26 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 27 | "name": " " 28 | }, 29 | "GetDocument": { 30 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 31 | "name": "projects/geoff-python/databases/(default)/documents/users/alovelace", 32 | "document_mask_field_path": "alovelace" 33 | }, 34 | "ListCollectionIds": { 35 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 36 | "parent": "projects/geoff-python/databases/(default)/documents/test_document/my_chatroom" 37 | }, 38 | "ListDocuments": { 39 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 40 | "parent": "projects/geoff-python/databases/(default)/documents" 41 | }, 42 | "Rollback": { 43 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 44 | "database": "projects/geoff-python/databases/(default)" 45 | }, 46 | "RunQuery": { 47 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 48 | "parent": "projects/geoff-python/databases/(default)", 49 | "field_path": "foo" 50 | }, 51 | "UpdateDocument": { 52 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 53 | "name": "projects/geoff-python/databases/(default)/documents/users/alovelace" 54 | }, 55 | "Write": { 56 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 57 | "name": "projects/geoff-python/databases/(default)/documents/users/alovelace", 58 | "database": "projects/geoff-python/databases/(default)" 59 | }, 60 | "CreateIndex": { 61 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 62 | "name": "test", 63 | "parent": "projects/geoff-python/databases/(default)", 64 | "collection_id": "goya", 65 | "field_path1": "mom.mom1", 66 | "mode1": "ASCENDING", 67 | "field_path2": "stefen.stefen1", 68 | "mode2": "ASCENDING" 69 | }, 70 | "DeleteIndex": { 71 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 72 | "parent": "projects/geoff-python/databases/(default)" 73 | }, 74 | "GetIndex": { 75 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 76 | "parent": "projects/geoff-python/databases/(default)", 77 | "name": "projects/geoff-python/databases/(default)" 78 | }, 79 | "ListIndex": { 80 | "credentials": "/opt/Geoff-python-c2e9f0b91290.json", 81 | "parent": "projects/geoff-python/databases/(default)" 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /firestore/examples/end2end/src/grpcCtrl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import sys 5 | 6 | import subprocess 7 | import BatchGetDocuments 8 | import BeginTransaction 9 | import Commit 10 | import CreateDocument 11 | import DeleteDocument 12 | import GetDocument 13 | import ListCollectionIds 14 | import ListDocuments 15 | import Rollback 16 | import RunQuery 17 | import UpdateDocument 18 | import CreateIndex 19 | import DeleteIndex 20 | import GetIndex 21 | import ListIndex 22 | import Write 23 | 24 | 25 | subprocess.call('clear') 26 | 27 | print(" Google Firestore RPC Menu \n") 28 | print("1|batchgetdocuments ......... BatchGetDocuments") 29 | print("2|begintransaction ......... BeginTransaction") 30 | print("3|commit .................... Commit") 31 | print("4|createdocument ............ CreateDocument") 32 | print("5|deletedocument ............ DeleteDocument") 33 | print("6|getdocument ............... GetDocument") 34 | print("7|listcollectionids ......... ListCollectionIds") 35 | print("8|listdocuments ............. ListDocuments") 36 | print("9|rollback .................. Rollback") 37 | print("10|runquery ................. RunQuery") 38 | print("11|updatedocument ........... UpdateDocument") 39 | print("12|write..................... Write\n") 40 | print(" Firestore Admin RPC's \n") 41 | print("13|createindex .............. CreateIndex") 42 | print("14|deleteindex .............. DeleteIndex") 43 | print("15|getindex ................. GetIndex") 44 | print("16|listindex ................ ListIndex\n") 45 | 46 | grpc = raw_input("Enter an Option: ") 47 | 48 | if grpc == 'batchgetdocuments' or grpc == '1': 49 | BatchGetDocuments.main() 50 | 51 | if grpc == 'begintransaction' or grpc == '2': 52 | BeginTransaction.main() 53 | 54 | if grpc == 'commit' or grpc == '3': 55 | Commit.main() 56 | 57 | if grpc == 'createdocument' or grpc == '4': 58 | CreateDocument.main() 59 | 60 | if grpc == 'deletedocument' or grpc == '5': 61 | DeleteDocument.main() 62 | 63 | if grpc == 'getdocument' or grpc == '6': 64 | GetDocument.main() 65 | 66 | if grpc == 'listcollectionids' or grpc == '7': 67 | ListCollectionIds.main() 68 | 69 | if grpc == 'listdocuments' or grpc == '8': 70 | ListDocuments.main() 71 | if grpc == 'rollback' or grpc == '9': 72 | Rollback.main() 73 | 74 | if grpc == 'runquery' or grpc == '10': 75 | RunQuery.main() 76 | 77 | if grpc == 'updatedocument' or grpc == '11': 78 | UpdateDocument.main() 79 | 80 | if grpc == 'write' or grpc == '12': 81 | Write.main() 82 | 83 | if grpc == 'createindex' or grpc == '13': 84 | CreateIndex.main() 85 | 86 | if grpc == 'deleteindex' or grpc == '14': 87 | DeleteIndex.main() 88 | 89 | if grpc == 'getindex' or grpc == '15': 90 | GetIndex.main() 91 | 92 | if grpc == 'listindex' or grpc == '16': 93 | ListIndex.main() 94 | 95 | if grpc == 'runquery' or grpc == '17': 96 | RunQuery.main() 97 | -------------------------------------------------------------------------------- /kokoro/presubmit.cfg: -------------------------------------------------------------------------------- 1 | build_file: "grpc-gcp-python/src/setup.sh" -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | version.py 2 | build/ 3 | dist/ 4 | *.egg-info/ 5 | -------------------------------------------------------------------------------- /src/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | v0.2.2 5 | ------ 6 | 7 | - Readme fix. 8 | - Setup universal wheels. 9 | 10 | v0.2.1 11 | ------ 12 | 13 | - Apply RLock for Channel class. 14 | - Fix corner cases for async future calls. 15 | - Apply unit tests of grpc channel to grpc_gcp channel. 16 | 17 | v0.2.0 18 | ------ 19 | 20 | - Added ``grpc_gcp.api_config_from_text_pb``: Create an instance of ApiConfig given a protocal message string defining api configurations. 21 | 22 | v0.1.1 23 | ------ 24 | 25 | Initial release with core functionalities. 26 | 27 | - ``grpc_gcp.secure_channel``: Create a secure channel object with connection management. 28 | -------------------------------------------------------------------------------- /src/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst CHANGELOG.rst LICENSE 2 | include version.py 3 | recursive-include grpc_gcp *.py 4 | global-exclude *.pyc __pycache__ 5 | -------------------------------------------------------------------------------- /src/README.rst: -------------------------------------------------------------------------------- 1 | gRPC-GCP Python 2 | =============== 3 | 4 | Package for gRPC-GCP Python. 5 | 6 | Installation 7 | ------------ 8 | 9 | gRPC-GCP Python is available wherever gRPC is available. 10 | 11 | From PyPI 12 | ~~~~~~~~~ 13 | 14 | If you are installing locally... 15 | 16 | :: 17 | 18 | $ pip install grpcio-gcp 19 | 20 | Else system wide (on Ubuntu)... 21 | 22 | :: 23 | 24 | $ sudo pip install grpcio-gcp 25 | 26 | Usage 27 | ----- 28 | 29 | Create a config file (e.g. ``spanner.grpc.config``) defining API configuration, 30 | with ChannelPoolConfig and MethodConfig. 31 | 32 | :: 33 | 34 | channel_pool: { 35 | max_size: 10 36 | max_concurrent_streams_low_watermark: 1 37 | } 38 | method: { 39 | name: "/google.spanner.v1.Spanner/CreateSession" 40 | affinity: { 41 | command: BIND 42 | affinity_key: "name" 43 | } 44 | } 45 | method: { 46 | name: "/google.spanner.v1.Spanner/GetSession" 47 | affinity: { 48 | command: BOUND 49 | affinity_key: "name" 50 | } 51 | } 52 | method: { 53 | name: "/google.spanner.v1.Spanner/DeleteSession" 54 | affinity: { 55 | command: UNBIND 56 | affinity_key: "name" 57 | } 58 | } 59 | 60 | Load configuration file to ApiConfig object. 61 | 62 | .. code-block:: python 63 | 64 | import google.protobuf.text_format 65 | 66 | config = grpc_gcp.api_config_from_text_pb( 67 | pkg_resources.resource_string(__name__, 'spanner.grpc.config')) 68 | 69 | Create channel pool using grpc_gcp. 70 | 71 | .. code-block:: python 72 | 73 | import grpc_gcp 74 | import grpc 75 | 76 | credentials = grpc.ssl_channel_credentials() 77 | # Add api config key-value pair to options 78 | options = [(grpc_gcp.API_CONFIG_CHANNEL_ARG, config)] 79 | channel_pool = grpc_gcp.secure_channel(target, credentials, options) 80 | 81 | The generated channel pool is inherited from the original grpc.Channel, 82 | with underlying support for multiple grpc channels. 83 | -------------------------------------------------------------------------------- /src/grpc_gcp.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 gRPC authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package grpc.gcp; 18 | 19 | message ApiConfig { 20 | // The channel pool configurations. 21 | ChannelPoolConfig channel_pool = 2; 22 | 23 | // The method configurations. 24 | repeated MethodConfig method = 1001; 25 | } 26 | 27 | message ChannelPoolConfig { 28 | // The max number of channels in the pool. 29 | uint32 max_size = 1; 30 | // The idle timeout (seconds) of channels without bound affinity sessions. 31 | uint64 idle_timeout = 2; 32 | // The low watermark of max number of concurrent streams in a channel. 33 | // New channel will be created once it get hit, until we reach the max size 34 | // of the channel pool. 35 | uint32 max_concurrent_streams_low_watermark = 3; 36 | } 37 | 38 | message MethodConfig { 39 | // A fully qualified name of a gRPC method, or a wildcard pattern ending 40 | // with .*, such as foo.bar.A, foo.bar.*. Method configs are evaluated 41 | // sequentially, and the first one takes precedence. 42 | repeated string name = 1; 43 | 44 | // The channel affinity configurations. 45 | AffinityConfig affinity = 1001; 46 | } 47 | 48 | message AffinityConfig { 49 | enum Command { 50 | // The annotated method will be required to be bound to an existing session 51 | // to execute the RPC. The corresponding will be 52 | // used to find the affinity key from the request message. 53 | BOUND = 0; 54 | // The annotated method will establish the channel affinity with the channel 55 | // which is used to execute the RPC. The corresponding 56 | // will be used to find the affinity key from the 57 | // response message. 58 | BIND = 1; 59 | // The annotated method will remove the channel affinity with the channel 60 | // which is used to execute the RPC. The corresponding 61 | // will be used to find the affinity key from the 62 | // request message. 63 | UNBIND = 2; 64 | } 65 | // The affinity command applies on the selected gRPC methods. 66 | Command command = 2; 67 | // The field path of the affinity key in the request/response message. 68 | // For example: "f.a", "f.b.d", etc. 69 | string affinity_key = 3; 70 | } 71 | -------------------------------------------------------------------------------- /src/grpc_gcp/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 gRPC-GCP authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import grpc 15 | from google.protobuf import text_format 16 | from grpc_gcp import _channel 17 | from grpc_gcp.proto import grpc_gcp_pb2 18 | 19 | # The channel argument to for gRPC-GCP API config. 20 | # Its value must be a valid ApiConfig proto defined in grpc_gcp.proto. 21 | API_CONFIG_CHANNEL_ARG = 'grpc_gcp.api_config' 22 | 23 | 24 | def secure_channel(target, credentials, options=None): 25 | """Creates a secure Channel to a server. 26 | 27 | Args: 28 | target: The server address. 29 | credentials: A ChannelCredentials instance. 30 | options: An optional list of key-value pairs (channel args 31 | in gRPC Core runtime) to configure the channel. 32 | 33 | Returns: 34 | A Channel object. 35 | """ 36 | if options and [arg for arg in options 37 | if arg[0] == API_CONFIG_CHANNEL_ARG]: 38 | return _channel.Channel(target, () 39 | if options is None else options, credentials) 40 | else: 41 | return grpc.secure_channel(target, credentials, options) 42 | 43 | 44 | def insecure_channel(target, options=None): 45 | """Creates an insecure Channel to a server. 46 | 47 | Args: 48 | target: The server address. 49 | options: An optional list of key-value pairs (channel args 50 | in gRPC Core runtime) to configure the channel. 51 | 52 | Returns: 53 | A Channel object. 54 | """ 55 | if options and [arg for arg in options 56 | if arg[0] == API_CONFIG_CHANNEL_ARG]: 57 | return _channel.Channel(target, options) 58 | else: 59 | return grpc.insecure_channel(target, options) 60 | 61 | 62 | def api_config_from_text_pb(text_pb): 63 | """Creates an instance of ApiConfig with provided api configuration. 64 | 65 | Args: 66 | text_pb: The text representation of a protocol message defining api 67 | configuration. 68 | 69 | Returns: 70 | A grpc_gcp_pb2.ApiConfig object. 71 | """ 72 | config = grpc_gcp_pb2.ApiConfig() 73 | text_format.Merge(text_pb, config) 74 | return config 75 | -------------------------------------------------------------------------------- /src/grpc_gcp/proto/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/grpc-gcp-python/5a2cd9807bbaf1b85402a2a364775e5b65853df6/src/grpc_gcp/proto/__init__.py -------------------------------------------------------------------------------- /src/requirements.txt: -------------------------------------------------------------------------------- 1 | grpcio 2 | grpcio-tools 3 | -------------------------------------------------------------------------------- /src/setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /src/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 gRPC-GCP authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import setuptools 15 | import version 16 | 17 | LICENSE = 'Apache License 2.0' 18 | 19 | CLASSIFIERS = [ 20 | 'Development Status :: 5 - Production/Stable', 21 | 'Programming Language :: Python', 22 | 'Programming Language :: Python :: 2', 23 | 'Programming Language :: Python :: 2.7', 24 | 'Programming Language :: Python :: 3', 25 | 'Programming Language :: Python :: 3.4', 26 | 'Programming Language :: Python :: 3.5', 27 | 'Programming Language :: Python :: 3.6', 28 | 'License :: OSI Approved :: Apache Software License', 29 | ] 30 | 31 | INSTALL_REQUIRES = [ 32 | 'grpcio>={version}'.format(version=version.GRPC), 33 | 'protobuf>=3.6.1', 34 | ] 35 | 36 | setuptools.setup( 37 | name='grpcio-gcp', 38 | version=version.GRPC_GCP, 39 | description='gRPC extensions for Google Cloud Platform', 40 | author='The gRPC-GCP Authors', 41 | author_email='grpc-io@googlegroups.com', 42 | url='https://grpc.io', 43 | long_description=open('README.rst').read(), 44 | license=LICENSE, 45 | classifiers=CLASSIFIERS, 46 | packages=setuptools.find_packages(), 47 | include_package_data=True, 48 | install_requires=INSTALL_REQUIRES, 49 | ) 50 | -------------------------------------------------------------------------------- /src/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | rm -rf build 4 | rm -rf dist 5 | rm -rf *.egg-info 6 | pip uninstall grpcio-gcp -y 7 | pip install -rrequirements.txt 8 | cp -f ../template/version.py version.py 9 | python -m grpc_tools.protoc -I. --python_out=grpc_gcp/proto grpc_gcp.proto 10 | python setup.py sdist bdist_wheel 11 | pip install dist/grpcio-gcp-*.tar.gz 12 | -------------------------------------------------------------------------------- /template/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | __import__('pkg_resources').declare_namespace(__name__) 3 | except ImportError: 4 | __path__ = __import__('pkgutil').extend_path(__path__, __name__) 5 | -------------------------------------------------------------------------------- /template/version.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 gRPC-GCP authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | GRPC = '1.12.0' 15 | GRPC_GCP = '0.2.2' 16 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | version.py 2 | google 3 | -------------------------------------------------------------------------------- /tests/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include grpc_gcp_test *.py *.config 2 | recursive-include grpc_gcp_benchmark *.py *.config 3 | recursive-include google *.py 4 | global-exclude *.pyc 5 | -------------------------------------------------------------------------------- /tests/benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | python -m grpc_gcp_test.benchmark.spanner_benchmark "$@" 4 | -------------------------------------------------------------------------------- /tests/codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | rm -rf google 4 | for p in $(find ../third_party/googleapis/google -type f -name *.proto); do 5 | python \ 6 | -m grpc_tools.protoc \ 7 | -I../third_party/googleapis/ \ 8 | --python_out=. \ 9 | --grpc_python_out=. \ 10 | ${p} 11 | done 12 | 13 | for d in $(find google -type d); do 14 | touch "${d}/__init__.py" 15 | done 16 | 17 | cp -f ../template/__init__.py google 18 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/benchmark/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/grpc-gcp-python/5a2cd9807bbaf1b85402a2a364775e5b65853df6/tests/grpc_gcp_test/benchmark/__init__.py -------------------------------------------------------------------------------- /tests/grpc_gcp_test/benchmark/spanner.grpc.config: -------------------------------------------------------------------------------- 1 | channel_pool: { 2 | max_size: 10 3 | } 4 | method: { 5 | name: "/google.spanner.v1.Spanner/CreateSession" 6 | affinity: { 7 | command: BIND 8 | affinity_key: "name" 9 | } 10 | } 11 | method: { 12 | name: "/google.spanner.v1.Spanner/GetSession" 13 | affinity: { 14 | command: BOUND 15 | affinity_key: "name" 16 | } 17 | } 18 | method: { 19 | name: "/google.spanner.v1.Spanner/DeleteSession" 20 | affinity: { 21 | command: UNBIND 22 | affinity_key: "name" 23 | } 24 | } 25 | method: { 26 | name: "/google.spanner.v1.Spanner/ExecuteSql" 27 | affinity: { 28 | command: BOUND 29 | affinity_key: "session" 30 | } 31 | } 32 | method: { 33 | name: "/google.spanner.v1.Spanner/ExecuteStreamingSql" 34 | affinity: { 35 | command: BOUND 36 | affinity_key: "session" 37 | } 38 | } 39 | method: { 40 | name: "/google.spanner.v1.Spanner/Read" 41 | affinity: { 42 | command: BOUND 43 | affinity_key: "session" 44 | } 45 | } 46 | method: { 47 | name: "/google.spanner.v1.Spanner/StreamingRead" 48 | affinity: { 49 | command: BOUND 50 | affinity_key: "session" 51 | } 52 | } 53 | method: { 54 | name: "/google.spanner.v1.Spanner/BeginTransaction" 55 | affinity: { 56 | command: BOUND 57 | affinity_key: "session" 58 | } 59 | } 60 | method: { 61 | name: "/google.spanner.v1.Spanner/Commit" 62 | affinity: { 63 | command: BOUND 64 | affinity_key: "session" 65 | } 66 | } 67 | method: { 68 | name: "/google.spanner.v1.Spanner/Rollback" 69 | affinity: { 70 | command: BOUND 71 | affinity_key: "session" 72 | } 73 | } 74 | method: { 75 | name: "/google.spanner.v1.Spanner/PartitionQuery" 76 | affinity: { 77 | command: BOUND 78 | affinity_key: "session" 79 | } 80 | } 81 | method: { 82 | name: "/google.spanner.v1.Spanner/PartitionRead" 83 | affinity: { 84 | command: BOUND 85 | affinity_key: "session" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/grpc-gcp-python/5a2cd9807bbaf1b85402a2a364775e5b65853df6/tests/grpc_gcp_test/integration/__init__.py -------------------------------------------------------------------------------- /tests/grpc_gcp_test/integration/spanner.grpc.config: -------------------------------------------------------------------------------- 1 | channel_pool: { 2 | max_size: 10 3 | max_concurrent_streams_low_watermark: 1 4 | } 5 | method: { 6 | name: "/google.spanner.v1.Spanner/CreateSession" 7 | affinity: { 8 | command: BIND 9 | affinity_key: "name" 10 | } 11 | } 12 | method: { 13 | name: "/google.spanner.v1.Spanner/GetSession" 14 | affinity: { 15 | command: BOUND 16 | affinity_key: "name" 17 | } 18 | } 19 | method: { 20 | name: "/google.spanner.v1.Spanner/DeleteSession" 21 | affinity: { 22 | command: UNBIND 23 | affinity_key: "name" 24 | } 25 | } 26 | method: { 27 | name: "/google.spanner.v1.Spanner/ExecuteSql" 28 | affinity: { 29 | command: BOUND 30 | affinity_key: "session" 31 | } 32 | } 33 | method: { 34 | name: "/google.spanner.v1.Spanner/ExecuteStreamingSql" 35 | affinity: { 36 | command: BOUND 37 | affinity_key: "session" 38 | } 39 | } 40 | method: { 41 | name: "/google.spanner.v1.Spanner/Read" 42 | affinity: { 43 | command: BOUND 44 | affinity_key: "session" 45 | } 46 | } 47 | method: { 48 | name: "/google.spanner.v1.Spanner/StreamingRead" 49 | affinity: { 50 | command: BOUND 51 | affinity_key: "session" 52 | } 53 | } 54 | method: { 55 | name: "/google.spanner.v1.Spanner/BeginTransaction" 56 | affinity: { 57 | command: BOUND 58 | affinity_key: "session" 59 | } 60 | } 61 | method: { 62 | name: "/google.spanner.v1.Spanner/Commit" 63 | affinity: { 64 | command: BOUND 65 | affinity_key: "session" 66 | } 67 | } 68 | method: { 69 | name: "/google.spanner.v1.Spanner/Rollback" 70 | affinity: { 71 | command: BOUND 72 | affinity_key: "session" 73 | } 74 | } 75 | method: { 76 | name: "/google.spanner.v1.Spanner/PartitionQuery" 77 | affinity: { 78 | command: BOUND 79 | affinity_key: "session" 80 | } 81 | } 82 | method: { 83 | name: "/google.spanner.v1.Spanner/PartitionRead" 84 | affinity: { 85 | command: BOUND 86 | affinity_key: "session" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/stress/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/grpc-gcp-python/5a2cd9807bbaf1b85402a2a364775e5b65853df6/tests/grpc_gcp_test/stress/__init__.py -------------------------------------------------------------------------------- /tests/grpc_gcp_test/stress/client.py: -------------------------------------------------------------------------------- 1 | import random 2 | import sys 3 | import threading 4 | import timeit 5 | import traceback 6 | 7 | import google.protobuf.struct_pb2 8 | import google.protobuf.text_format 9 | import grpc 10 | import grpc_gcp 11 | import pkg_resources 12 | from absl import flags 13 | from google.auth.transport.grpc import AuthMetadataPlugin 14 | from google.auth.transport.requests import Request 15 | from google.spanner.v1 import spanner_pb2_grpc 16 | from grpc_gcp_test.stress import spanner_test_cases, stackdriver_util 17 | 18 | from six.moves import queue 19 | 20 | _TARGET = 'spanner.googleapis.com' 21 | _OAUTH_SCOPE = 'https://www.googleapis.com/auth/cloud-platform' 22 | 23 | FLAGS = flags.FLAGS 24 | flags.DEFINE_string('weighted_cases', 'execute_sql:100', 25 | 'comma seperated list of testcase:weighting') 26 | flags.DEFINE_string('api', 'spanner', 27 | 'name of cloud api for stress testing') 28 | flags.DEFINE_integer('num_channels_per_target', 1, 29 | 'number of channels per target') 30 | flags.DEFINE_integer('num_stubs_per_channel', 1, 31 | 'number of stubs to create per channel') 32 | flags.DEFINE_integer('timeout_secs', -1, 33 | 'timeout in seconds for the stress test') 34 | flags.DEFINE_boolean('gcp', False, 'load grpc gcp extension') 35 | 36 | 37 | util = stackdriver_util.StackdriverUtil() 38 | 39 | class TestRunner(threading.Thread): 40 | def __init__(self, stub, weighted_test_cases, exception_queue, stop_event): 41 | super(TestRunner, self).__init__() 42 | self._exception_queue = exception_queue 43 | self._stop_event = stop_event 44 | self._stub = stub 45 | self._test_cases_generator = _weighted_test_case_generator(weighted_test_cases) 46 | 47 | def run(self): 48 | while not self._stop_event.is_set(): 49 | try: 50 | test_case = next(self._test_cases_generator) 51 | start_time = timeit.default_timer() 52 | test_case(self._stub) 53 | end_time = timeit.default_timer() 54 | duration_ms = (end_time - start_time) * 1000 55 | sys.stdout.write('.') 56 | sys.stdout.flush() 57 | util.add_timeseries(FLAGS.api, test_case.__name__, end_time, duration_ms) 58 | except Exception as e: 59 | traceback.print_exc() 60 | self._exception_queue.put( 61 | Exception("An exception occured during test {}" 62 | .format(test_case), e)) 63 | 64 | 65 | def _weighted_test_case_generator(weighted_cases): 66 | weight_sum = sum(weighted_cases.itervalues()) 67 | 68 | while True: 69 | val = random.uniform(0, weight_sum) 70 | partial_sum = 0 71 | for case in weighted_cases: 72 | partial_sum += weighted_cases[case] 73 | if val <= partial_sum: 74 | yield case 75 | break 76 | 77 | 78 | def _create_channel(): 79 | http_request = Request() 80 | credentials, _ = google.auth.default([_OAUTH_SCOPE], http_request) 81 | 82 | if FLAGS.gcp: 83 | config = grpc_gcp.api_config_from_text_pb( 84 | pkg_resources.resource_string(__name__, 'spanner.grpc.config')) 85 | channel = _create_secure_gcp_channel( 86 | credentials, 87 | http_request, 88 | _TARGET, 89 | options=[(grpc_gcp.API_CONFIG_CHANNEL_ARG, config)]) 90 | else: 91 | channel = _create_secure_gcp_channel( 92 | credentials, http_request, _TARGET) 93 | 94 | return channel 95 | 96 | 97 | def _create_secure_gcp_channel( 98 | credentials, request, target, ssl_credentials=None, **kwargs): 99 | # This method is copied from 100 | # google.auth.transport.grpc.secure_authorized_channel but using 101 | # grpc_gcp.secure_channel to create the channel. 102 | metadata_plugin = AuthMetadataPlugin(credentials, request) 103 | google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin) 104 | if ssl_credentials is None: 105 | ssl_credentials = grpc.ssl_channel_credentials() 106 | composite_credentials = grpc.composite_channel_credentials( 107 | ssl_credentials, google_auth_credentials) 108 | return grpc_gcp.secure_channel(target, composite_credentials, **kwargs) 109 | 110 | 111 | def _parse_weighted_test_cases(test_case_args): 112 | weighted_test_cases = {} 113 | for test_case_arg in test_case_args.split(','): 114 | name, weight = test_case_arg.split(':', 1) 115 | test_case = spanner_test_cases.TEST_CASES[name] 116 | weighted_test_cases[test_case] = int(weight) 117 | return weighted_test_cases 118 | 119 | 120 | def run_test(): 121 | weighted_test_cases = _parse_weighted_test_cases(FLAGS.weighted_cases) 122 | exception_queue = queue.Queue() 123 | stop_event = threading.Event() 124 | runners = [] 125 | 126 | for _ in xrange(FLAGS.num_channels_per_target): 127 | channel = _create_channel() 128 | for _ in xrange(FLAGS.num_stubs_per_channel): 129 | stub = spanner_pb2_grpc.SpannerStub(channel) 130 | runner = TestRunner(stub, weighted_test_cases, exception_queue, stop_event) 131 | runners.append(runner) 132 | 133 | for runner in runners: 134 | runner.start() 135 | 136 | try: 137 | timeout = FLAGS.timeout_secs if FLAGS.timeout_secs >= 0 else None 138 | raise exception_queue.get(block=True, timeout=timeout) 139 | except queue.Empty: 140 | pass 141 | finally: 142 | stop_event.set() 143 | for runner in runners: 144 | runner.join() 145 | runner = None 146 | 147 | 148 | if __name__ == '__main__': 149 | FLAGS(sys.argv) 150 | run_test() 151 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/stress/spanner.grpc.config: -------------------------------------------------------------------------------- 1 | channel_pool: { 2 | max_size: 10 3 | max_concurrent_streams_low_watermark: 1 4 | } 5 | method: { 6 | name: "/google.spanner.v1.Spanner/CreateSession" 7 | affinity: { 8 | command: BIND 9 | affinity_key: "name" 10 | } 11 | } 12 | method: { 13 | name: "/google.spanner.v1.Spanner/GetSession" 14 | affinity: { 15 | command: BOUND 16 | affinity_key: "name" 17 | } 18 | } 19 | method: { 20 | name: "/google.spanner.v1.Spanner/DeleteSession" 21 | affinity: { 22 | command: UNBIND 23 | affinity_key: "name" 24 | } 25 | } 26 | method: { 27 | name: "/google.spanner.v1.Spanner/ExecuteSql" 28 | affinity: { 29 | command: BOUND 30 | affinity_key: "session" 31 | } 32 | } 33 | method: { 34 | name: "/google.spanner.v1.Spanner/ExecuteStreamingSql" 35 | affinity: { 36 | command: BOUND 37 | affinity_key: "session" 38 | } 39 | } 40 | method: { 41 | name: "/google.spanner.v1.Spanner/Read" 42 | affinity: { 43 | command: BOUND 44 | affinity_key: "session" 45 | } 46 | } 47 | method: { 48 | name: "/google.spanner.v1.Spanner/StreamingRead" 49 | affinity: { 50 | command: BOUND 51 | affinity_key: "session" 52 | } 53 | } 54 | method: { 55 | name: "/google.spanner.v1.Spanner/BeginTransaction" 56 | affinity: { 57 | command: BOUND 58 | affinity_key: "session" 59 | } 60 | } 61 | method: { 62 | name: "/google.spanner.v1.Spanner/Commit" 63 | affinity: { 64 | command: BOUND 65 | affinity_key: "session" 66 | } 67 | } 68 | method: { 69 | name: "/google.spanner.v1.Spanner/Rollback" 70 | affinity: { 71 | command: BOUND 72 | affinity_key: "session" 73 | } 74 | } 75 | method: { 76 | name: "/google.spanner.v1.Spanner/PartitionQuery" 77 | affinity: { 78 | command: BOUND 79 | affinity_key: "session" 80 | } 81 | } 82 | method: { 83 | name: "/google.spanner.v1.Spanner/PartitionRead" 84 | affinity: { 85 | command: BOUND 86 | affinity_key: "session" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/stress/spanner_test_cases.py: -------------------------------------------------------------------------------- 1 | from google.spanner.v1 import spanner_pb2 2 | 3 | _DATABASE = 'projects/grpc-gcp/instances/sample/databases/benchmark' 4 | _TEST_SQL = 'select id from storage' 5 | _TEST_COLUMN_DATA = 'payload' 6 | _TIMEOUT = 60 * 60 * 24 7 | 8 | def test_execute_sql(stub): 9 | session = stub.CreateSession( 10 | spanner_pb2.CreateSessionRequest(database=_DATABASE)) 11 | stub.ExecuteSql( 12 | spanner_pb2.ExecuteSqlRequest(session=session.name, 13 | sql=_TEST_SQL)) 14 | stub.DeleteSession( 15 | spanner_pb2.DeleteSessionRequest(name=session.name)) 16 | 17 | 18 | def test_execute_sql_async(stub): 19 | session = stub.CreateSession( 20 | spanner_pb2.CreateSessionRequest(database=_DATABASE)) 21 | response_future = stub.ExecuteSql.future( 22 | spanner_pb2.ExecuteSqlRequest(session=session.name, 23 | sql=_TEST_SQL)) 24 | response_future.result() 25 | stub.DeleteSession( 26 | spanner_pb2.DeleteSessionRequest(name=session.name)) 27 | 28 | 29 | def test_execute_streaming_sql(stub): 30 | session = stub.CreateSession( 31 | spanner_pb2.CreateSessionRequest(database=_DATABASE)) 32 | rendezvous = stub.ExecuteStreamingSql( 33 | spanner_pb2.ExecuteSqlRequest( 34 | session=session.name, 35 | sql=_TEST_SQL)) 36 | for _ in rendezvous: 37 | pass 38 | stub.DeleteSession( 39 | spanner_pb2.DeleteSessionRequest(name=session.name)) 40 | 41 | 42 | def test_list_sessions(stub): 43 | stub.ListSessions( 44 | spanner_pb2.ListSessionsRequest(database=_DATABASE)) 45 | 46 | def test_list_sessions_async(stub): 47 | resp_future = stub.ListSessions.future( 48 | spanner_pb2.ListSessionsRequest(database=_DATABASE), 49 | _TIMEOUT) 50 | resp_future.result() 51 | 52 | 53 | TEST_CASES = { 54 | 'execute_sql': test_execute_sql, 55 | 'execute_streaming_sql':test_execute_streaming_sql, 56 | 'execute_sql_async': test_execute_sql_async, 57 | 'list_sessions_async': test_list_sessions_async, 58 | 'list_sessions': test_list_sessions, 59 | } 60 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/stress/stackdriver_util.py: -------------------------------------------------------------------------------- 1 | from google.cloud import monitoring_v3 2 | 3 | _PROJECT_ID = 'grpc-gcp' 4 | _INSTANCE_ID = 'test-instance' 5 | _INSTANCE_ZONE = 'us-central1-c' 6 | _METRIC_TYPE = 'custom.googleapis.com/stress_test' 7 | _RESOURCE_TYPE = 'gce_instance' 8 | 9 | class StackdriverUtil: 10 | def __init__(self): 11 | self._client = monitoring_v3.MetricServiceClient() 12 | self._project_path = self._client.project_path(_PROJECT_ID) 13 | 14 | 15 | def add_timeseries(self, api, test_case, timestamp, duration_ms): 16 | series = monitoring_v3.types.TimeSeries() 17 | series.metric.type = '{}/{}/{}'.format(_METRIC_TYPE, api, test_case) 18 | series.resource.type = _RESOURCE_TYPE 19 | series.resource.labels['instance_id'] = _INSTANCE_ID 20 | series.resource.labels['zone'] = _INSTANCE_ZONE 21 | 22 | point = series.points.add() 23 | point.value.double_value = duration_ms 24 | point.interval.end_time.seconds = int(timestamp) 25 | point.interval.end_time.nanos = int( 26 | (timestamp - point.interval.end_time.seconds) * 10**9 27 | ) 28 | 29 | self._client.create_time_series(self._project_path, [series]) 30 | 31 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/grpc-gcp-python/5a2cd9807bbaf1b85402a2a364775e5b65853df6/tests/grpc_gcp_test/unit/__init__.py -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/_auth_context_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests exposure of SSL auth context""" 15 | 16 | import pickle 17 | import unittest 18 | 19 | import grpc 20 | import grpc_gcp 21 | from grpc import _channel 22 | import six 23 | 24 | from grpc_gcp_test.unit import test_common 25 | from grpc_gcp_test.unit import resources 26 | 27 | _REQUEST = b'\x00\x00\x00' 28 | _RESPONSE = b'\x00\x00\x00' 29 | 30 | _UNARY_UNARY = '/test/UnaryUnary' 31 | 32 | _SERVER_HOST_OVERRIDE = 'foo.test.google.fr' 33 | _CLIENT_IDS = ( 34 | b'*.test.google.fr', 35 | b'waterzooi.test.google.be', 36 | b'*.test.youtube.com', 37 | b'192.168.1.3', 38 | ) 39 | _ID = 'id' 40 | _ID_KEY = 'id_key' 41 | _AUTH_CTX = 'auth_ctx' 42 | 43 | _PRIVATE_KEY = resources.private_key() 44 | _CERTIFICATE_CHAIN = resources.certificate_chain() 45 | _TEST_ROOT_CERTIFICATES = resources.test_root_certificates() 46 | _SERVER_CERTS = ((_PRIVATE_KEY, _CERTIFICATE_CHAIN),) 47 | _PROPERTY_OPTIONS = (( 48 | 'grpc.ssl_target_name_override', 49 | _SERVER_HOST_OVERRIDE, 50 | ),) 51 | 52 | 53 | def handle_unary_unary(request, servicer_context): 54 | return pickle.dumps({ 55 | _ID: servicer_context.peer_identities(), 56 | _ID_KEY: servicer_context.peer_identity_key(), 57 | _AUTH_CTX: servicer_context.auth_context() 58 | }) 59 | 60 | 61 | class AuthContextTest(unittest.TestCase): 62 | 63 | def testInsecure(self): 64 | handler = grpc.method_handlers_generic_handler('test', { 65 | 'UnaryUnary': 66 | grpc.unary_unary_rpc_method_handler(handle_unary_unary) 67 | }) 68 | server = test_common.test_server() 69 | server.add_generic_rpc_handlers((handler,)) 70 | port = server.add_insecure_port('[::]:0') 71 | server.start() 72 | 73 | channel_config = grpc_gcp.api_config_from_text_pb('') 74 | channel = grpc_gcp.insecure_channel( 75 | 'localhost:%d' % port, 76 | options=((grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config),)) 77 | response = channel.unary_unary(_UNARY_UNARY)(_REQUEST) 78 | server.stop(None) 79 | 80 | auth_data = pickle.loads(response) 81 | self.assertIsNone(auth_data[_ID]) 82 | self.assertIsNone(auth_data[_ID_KEY]) 83 | self.assertDictEqual({}, auth_data[_AUTH_CTX]) 84 | 85 | def testSecureNoCert(self): 86 | handler = grpc.method_handlers_generic_handler('test', { 87 | 'UnaryUnary': 88 | grpc.unary_unary_rpc_method_handler(handle_unary_unary) 89 | }) 90 | server = test_common.test_server() 91 | server.add_generic_rpc_handlers((handler,)) 92 | server_cred = grpc.ssl_server_credentials(_SERVER_CERTS) 93 | port = server.add_secure_port('[::]:0', server_cred) 94 | server.start() 95 | 96 | channel_creds = grpc.ssl_channel_credentials( 97 | root_certificates=_TEST_ROOT_CERTIFICATES) 98 | channel_config = grpc_gcp.api_config_from_text_pb('') 99 | channel = grpc_gcp.secure_channel( 100 | 'localhost:{}'.format(port), 101 | channel_creds, 102 | options=_PROPERTY_OPTIONS + ( 103 | (grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config), 104 | ) 105 | ) 106 | response = channel.unary_unary(_UNARY_UNARY)(_REQUEST) 107 | server.stop(None) 108 | 109 | auth_data = pickle.loads(response) 110 | self.assertIsNone(auth_data[_ID]) 111 | self.assertIsNone(auth_data[_ID_KEY]) 112 | self.assertDictEqual({ 113 | 'transport_security_type': [b'ssl'], 114 | 'ssl_session_reused': [b'false'], 115 | }, auth_data[_AUTH_CTX]) 116 | 117 | def testSecureClientCert(self): 118 | handler = grpc.method_handlers_generic_handler('test', { 119 | 'UnaryUnary': 120 | grpc.unary_unary_rpc_method_handler(handle_unary_unary) 121 | }) 122 | server = test_common.test_server() 123 | server.add_generic_rpc_handlers((handler,)) 124 | server_cred = grpc.ssl_server_credentials( 125 | _SERVER_CERTS, 126 | root_certificates=_TEST_ROOT_CERTIFICATES, 127 | require_client_auth=True) 128 | port = server.add_secure_port('[::]:0', server_cred) 129 | server.start() 130 | 131 | channel_creds = grpc.ssl_channel_credentials( 132 | root_certificates=_TEST_ROOT_CERTIFICATES, 133 | private_key=_PRIVATE_KEY, 134 | certificate_chain=_CERTIFICATE_CHAIN) 135 | channel_config = grpc_gcp.api_config_from_text_pb('') 136 | channel = grpc_gcp.secure_channel( 137 | 'localhost:{}'.format(port), 138 | channel_creds, 139 | options=_PROPERTY_OPTIONS + ( 140 | (grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config), 141 | ) 142 | ) 143 | 144 | response = channel.unary_unary(_UNARY_UNARY)(_REQUEST) 145 | server.stop(None) 146 | 147 | auth_data = pickle.loads(response) 148 | auth_ctx = auth_data[_AUTH_CTX] 149 | six.assertCountEqual(self, _CLIENT_IDS, auth_data[_ID]) 150 | self.assertEqual('x509_subject_alternative_name', auth_data[_ID_KEY]) 151 | self.assertSequenceEqual([b'ssl'], auth_ctx['transport_security_type']) 152 | self.assertSequenceEqual([b'*.test.google.com'], 153 | auth_ctx['x509_common_name']) 154 | 155 | 156 | if __name__ == '__main__': 157 | unittest.main(verbosity=2) 158 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/_channel_connectivity_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests of grpc._channel.Channel connectivity.""" 15 | 16 | import threading 17 | import time 18 | import unittest 19 | 20 | import grpc 21 | import grpc_gcp 22 | from grpc_gcp_test.unit.framework.common import test_constants 23 | from grpc_gcp_test.unit import _thread_pool 24 | 25 | 26 | def _ready_in_connectivities(connectivities): 27 | return grpc.ChannelConnectivity.READY in connectivities 28 | 29 | 30 | def _last_connectivity_is_not_ready(connectivities): 31 | return connectivities[-1] is not grpc.ChannelConnectivity.READY 32 | 33 | 34 | class _Callback(object): 35 | 36 | def __init__(self): 37 | self._condition = threading.Condition() 38 | self._connectivities = [] 39 | 40 | def update(self, connectivity): 41 | with self._condition: 42 | self._connectivities.append(connectivity) 43 | self._condition.notify() 44 | 45 | def connectivities(self): 46 | with self._condition: 47 | return tuple(self._connectivities) 48 | 49 | def block_until_connectivities_satisfy(self, predicate): 50 | with self._condition: 51 | while True: 52 | connectivities = tuple(self._connectivities) 53 | if predicate(connectivities): 54 | return connectivities 55 | else: 56 | self._condition.wait() 57 | 58 | 59 | class ChannelConnectivityTest(unittest.TestCase): 60 | 61 | def test_lonely_channel_connectivity(self): 62 | callback = _Callback() 63 | 64 | channel_config = grpc_gcp.api_config_from_text_pb('') 65 | channel = grpc_gcp.insecure_channel( 66 | 'localhost:12345', 67 | options=((grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config),)) 68 | channel.subscribe(callback.update, try_to_connect=False) 69 | time.sleep(4) 70 | first_connectivities = callback.block_until_connectivities_satisfy(bool) 71 | channel.subscribe(callback.update, try_to_connect=True) 72 | time.sleep(4) 73 | second_connectivities = callback.block_until_connectivities_satisfy( 74 | lambda connectivities: 2 <= len(connectivities)) 75 | # Wait for a connection that will never happen. 76 | time.sleep(test_constants.SHORT_TIMEOUT) 77 | third_connectivities = callback.connectivities() 78 | channel.unsubscribe(callback.update) 79 | fourth_connectivities = callback.connectivities() 80 | channel.unsubscribe(callback.update) 81 | fifth_connectivities = callback.connectivities() 82 | 83 | self.assertSequenceEqual((grpc.ChannelConnectivity.IDLE,), 84 | first_connectivities) 85 | self.assertNotIn(grpc.ChannelConnectivity.READY, second_connectivities) 86 | self.assertNotIn(grpc.ChannelConnectivity.READY, third_connectivities) 87 | self.assertNotIn(grpc.ChannelConnectivity.READY, fourth_connectivities) 88 | self.assertNotIn(grpc.ChannelConnectivity.READY, fifth_connectivities) 89 | 90 | def test_immediately_connectable_channel_connectivity(self): 91 | thread_pool = _thread_pool.RecordingThreadPool(max_workers=None) 92 | server = grpc.server(thread_pool, options=(('grpc.so_reuseport', 0),)) 93 | port = server.add_insecure_port('[::]:0') 94 | server.start() 95 | first_callback = _Callback() 96 | second_callback = _Callback() 97 | 98 | channel_config = grpc_gcp.api_config_from_text_pb('') 99 | channel = grpc_gcp.insecure_channel( 100 | 'localhost:{}'.format(port), 101 | options=((grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config),)) 102 | channel.subscribe(first_callback.update, try_to_connect=False) 103 | first_connectivities = first_callback.block_until_connectivities_satisfy( 104 | bool) 105 | # Wait for a connection that will never happen because try_to_connect=True 106 | # has not yet been passed. 107 | time.sleep(test_constants.SHORT_TIMEOUT) 108 | second_connectivities = first_callback.connectivities() 109 | channel.subscribe(second_callback.update, try_to_connect=True) 110 | third_connectivities = first_callback.block_until_connectivities_satisfy( 111 | lambda connectivities: 2 <= len(connectivities)) 112 | fourth_connectivities = second_callback.block_until_connectivities_satisfy( 113 | bool) 114 | # Wait for a connection that will happen (or may already have happened). 115 | first_callback.block_until_connectivities_satisfy( 116 | _ready_in_connectivities) 117 | second_callback.block_until_connectivities_satisfy( 118 | _ready_in_connectivities) 119 | del channel 120 | 121 | self.assertSequenceEqual((grpc.ChannelConnectivity.IDLE,), 122 | first_connectivities) 123 | self.assertSequenceEqual((grpc.ChannelConnectivity.IDLE,), 124 | second_connectivities) 125 | self.assertNotIn(grpc.ChannelConnectivity.TRANSIENT_FAILURE, 126 | third_connectivities) 127 | self.assertNotIn(grpc.ChannelConnectivity.SHUTDOWN, 128 | third_connectivities) 129 | self.assertNotIn(grpc.ChannelConnectivity.TRANSIENT_FAILURE, 130 | fourth_connectivities) 131 | self.assertNotIn(grpc.ChannelConnectivity.SHUTDOWN, 132 | fourth_connectivities) 133 | self.assertFalse(thread_pool.was_used()) 134 | 135 | def test_reachable_then_unreachable_channel_connectivity(self): 136 | thread_pool = _thread_pool.RecordingThreadPool(max_workers=None) 137 | server = grpc.server(thread_pool, options=(('grpc.so_reuseport', 0),)) 138 | port = server.add_insecure_port('[::]:0') 139 | server.start() 140 | callback = _Callback() 141 | 142 | channel_config = grpc_gcp.api_config_from_text_pb('') 143 | channel = grpc_gcp.insecure_channel( 144 | 'localhost:{}'.format(port), 145 | options=((grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config),)) 146 | channel.subscribe(callback.update, try_to_connect=True) 147 | callback.block_until_connectivities_satisfy(_ready_in_connectivities) 148 | # Now take down the server and confirm that channel readiness is repudiated. 149 | server.stop(None) 150 | callback.block_until_connectivities_satisfy( 151 | _last_connectivity_is_not_ready) 152 | channel.unsubscribe(callback.update) 153 | self.assertFalse(thread_pool.was_used()) 154 | 155 | 156 | if __name__ == '__main__': 157 | unittest.main(verbosity=2) 158 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/_channel_ready_future_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests of grpc.channel_ready_future.""" 15 | 16 | import threading 17 | import unittest 18 | 19 | import grpc 20 | import grpc_gcp 21 | from grpc_gcp_test.unit.framework.common import test_constants 22 | from grpc_gcp_test.unit import _thread_pool 23 | 24 | 25 | class _Callback(object): 26 | 27 | def __init__(self): 28 | self._condition = threading.Condition() 29 | self._value = None 30 | 31 | def accept_value(self, value): 32 | with self._condition: 33 | self._value = value 34 | self._condition.notify_all() 35 | 36 | def block_until_called(self): 37 | with self._condition: 38 | while self._value is None: 39 | self._condition.wait() 40 | return self._value 41 | 42 | 43 | class ChannelReadyFutureTest(unittest.TestCase): 44 | 45 | def test_lonely_channel_connectivity(self): 46 | callback = _Callback() 47 | channel_config = grpc_gcp.api_config_from_text_pb('') 48 | channel = grpc_gcp.insecure_channel( 49 | 'localhost:12345', 50 | options=((grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config),) 51 | ) 52 | ready_future = grpc.channel_ready_future(channel) 53 | ready_future.add_done_callback(callback.accept_value) 54 | with self.assertRaises(grpc.FutureTimeoutError): 55 | ready_future.result(timeout=test_constants.SHORT_TIMEOUT) 56 | self.assertFalse(ready_future.cancelled()) 57 | self.assertFalse(ready_future.done()) 58 | self.assertTrue(ready_future.running()) 59 | ready_future.cancel() 60 | value_passed_to_callback = callback.block_until_called() 61 | self.assertIs(ready_future, value_passed_to_callback) 62 | self.assertTrue(ready_future.cancelled()) 63 | self.assertTrue(ready_future.done()) 64 | self.assertFalse(ready_future.running()) 65 | 66 | def test_immediately_connectable_channel_connectivity(self): 67 | thread_pool = _thread_pool.RecordingThreadPool(max_workers=None) 68 | server = grpc.server(thread_pool, options=(('grpc.so_reuseport', 0),)) 69 | port = server.add_insecure_port('[::]:0') 70 | server.start() 71 | callback = _Callback() 72 | 73 | channel_config = grpc_gcp.api_config_from_text_pb('') 74 | channel = grpc_gcp.insecure_channel( 75 | 'localhost:{}'.format(port), 76 | options=((grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config),) 77 | ) 78 | 79 | ready_future = grpc.channel_ready_future(channel) 80 | ready_future.add_done_callback(callback.accept_value) 81 | self.assertIsNone( 82 | ready_future.result(timeout=test_constants.LONG_TIMEOUT)) 83 | value_passed_to_callback = callback.block_until_called() 84 | self.assertIs(ready_future, value_passed_to_callback) 85 | self.assertFalse(ready_future.cancelled()) 86 | self.assertTrue(ready_future.done()) 87 | self.assertFalse(ready_future.running()) 88 | # Cancellation after maturity has no effect. 89 | ready_future.cancel() 90 | self.assertFalse(ready_future.cancelled()) 91 | self.assertTrue(ready_future.done()) 92 | self.assertFalse(ready_future.running()) 93 | self.assertFalse(thread_pool.was_used()) 94 | 95 | 96 | if __name__ == '__main__': 97 | unittest.main(verbosity=2) 98 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/_empty_message_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | 17 | import grpc 18 | import grpc_gcp 19 | 20 | from grpc_gcp_test.unit import test_common 21 | from grpc_gcp_test.unit.framework.common import test_constants 22 | 23 | _REQUEST = b'' 24 | _RESPONSE = b'' 25 | 26 | _UNARY_UNARY = '/test/UnaryUnary' 27 | _UNARY_STREAM = '/test/UnaryStream' 28 | _STREAM_UNARY = '/test/StreamUnary' 29 | _STREAM_STREAM = '/test/StreamStream' 30 | 31 | 32 | def handle_unary_unary(request, servicer_context): 33 | return _RESPONSE 34 | 35 | 36 | def handle_unary_stream(request, servicer_context): 37 | for _ in range(test_constants.STREAM_LENGTH): 38 | yield _RESPONSE 39 | 40 | 41 | def handle_stream_unary(request_iterator, servicer_context): 42 | for request in request_iterator: 43 | pass 44 | return _RESPONSE 45 | 46 | 47 | def handle_stream_stream(request_iterator, servicer_context): 48 | for request in request_iterator: 49 | yield _RESPONSE 50 | 51 | 52 | class _MethodHandler(grpc.RpcMethodHandler): 53 | 54 | def __init__(self, request_streaming, response_streaming): 55 | self.request_streaming = request_streaming 56 | self.response_streaming = response_streaming 57 | self.request_deserializer = None 58 | self.response_serializer = None 59 | self.unary_unary = None 60 | self.unary_stream = None 61 | self.stream_unary = None 62 | self.stream_stream = None 63 | if self.request_streaming and self.response_streaming: 64 | self.stream_stream = handle_stream_stream 65 | elif self.request_streaming: 66 | self.stream_unary = handle_stream_unary 67 | elif self.response_streaming: 68 | self.unary_stream = handle_unary_stream 69 | else: 70 | self.unary_unary = handle_unary_unary 71 | 72 | 73 | class _GenericHandler(grpc.GenericRpcHandler): 74 | 75 | def service(self, handler_call_details): 76 | if handler_call_details.method == _UNARY_UNARY: 77 | return _MethodHandler(False, False) 78 | elif handler_call_details.method == _UNARY_STREAM: 79 | return _MethodHandler(False, True) 80 | elif handler_call_details.method == _STREAM_UNARY: 81 | return _MethodHandler(True, False) 82 | elif handler_call_details.method == _STREAM_STREAM: 83 | return _MethodHandler(True, True) 84 | else: 85 | return None 86 | 87 | 88 | class EmptyMessageTest(unittest.TestCase): 89 | 90 | def setUp(self): 91 | self._server = test_common.test_server() 92 | self._server.add_generic_rpc_handlers((_GenericHandler(),)) 93 | port = self._server.add_insecure_port('[::]:0') 94 | self._server.start() 95 | channel_config = grpc_gcp.api_config_from_text_pb('') 96 | self._channel = grpc_gcp.insecure_channel( 97 | 'localhost:{}'.format(port), 98 | options=((grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config),) 99 | ) 100 | 101 | def tearDown(self): 102 | self._server.stop(0) 103 | 104 | def testUnaryUnary(self): 105 | response = self._channel.unary_unary(_UNARY_UNARY)(_REQUEST) 106 | self.assertEqual(_RESPONSE, response) 107 | 108 | def testUnaryStream(self): 109 | response_iterator = self._channel.unary_stream(_UNARY_STREAM)(_REQUEST) 110 | self.assertSequenceEqual([_RESPONSE] * test_constants.STREAM_LENGTH, 111 | list(response_iterator)) 112 | 113 | def testStreamUnary(self): 114 | response = self._channel.stream_unary(_STREAM_UNARY)(iter( 115 | [_REQUEST] * test_constants.STREAM_LENGTH)) 116 | self.assertEqual(_RESPONSE, response) 117 | 118 | def testStreamStream(self): 119 | response_iterator = self._channel.stream_stream(_STREAM_STREAM)(iter( 120 | [_REQUEST] * test_constants.STREAM_LENGTH)) 121 | self.assertSequenceEqual([_RESPONSE] * test_constants.STREAM_LENGTH, 122 | list(response_iterator)) 123 | 124 | 125 | if __name__ == '__main__': 126 | unittest.main(verbosity=2) 127 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/_exit_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests clean exit of server/client on Python Interpreter exit/sigint. 15 | 16 | The tests in this module spawn a subprocess for each test case, the 17 | test is considered successful if it doesn't hang/timeout. 18 | """ 19 | 20 | import atexit 21 | import os 22 | import signal 23 | import six 24 | import subprocess 25 | import sys 26 | import threading 27 | import time 28 | import unittest 29 | 30 | from grpc_gcp_test.unit import _exit_scenarios 31 | 32 | SCENARIO_FILE = os.path.abspath( 33 | os.path.join( 34 | os.path.dirname(os.path.realpath(__file__)), '_exit_scenarios.py')) 35 | INTERPRETER = sys.executable 36 | BASE_COMMAND = [INTERPRETER, SCENARIO_FILE] 37 | BASE_SIGTERM_COMMAND = BASE_COMMAND + ['--wait_for_interrupt'] 38 | 39 | INIT_TIME = 1.0 40 | 41 | processes = [] 42 | process_lock = threading.Lock() 43 | 44 | 45 | # Make sure we attempt to clean up any 46 | # processes we may have left running 47 | def cleanup_processes(): 48 | with process_lock: 49 | for process in processes: 50 | try: 51 | process.kill() 52 | except Exception: 53 | pass 54 | 55 | 56 | atexit.register(cleanup_processes) 57 | 58 | 59 | def interrupt_and_wait(process): 60 | with process_lock: 61 | processes.append(process) 62 | time.sleep(INIT_TIME) 63 | os.kill(process.pid, signal.SIGINT) 64 | process.wait() 65 | 66 | 67 | def wait(process): 68 | with process_lock: 69 | processes.append(process) 70 | process.wait() 71 | 72 | 73 | @unittest.skip('https://github.com/grpc/grpc/issues/7311') 74 | class ExitTest(unittest.TestCase): 75 | 76 | def test_unstarted_server(self): 77 | process = subprocess.Popen( 78 | BASE_COMMAND + [_exit_scenarios.UNSTARTED_SERVER], 79 | stdout=sys.stdout, 80 | stderr=sys.stderr) 81 | wait(process) 82 | 83 | def test_unstarted_server_terminate(self): 84 | process = subprocess.Popen( 85 | BASE_SIGTERM_COMMAND + [_exit_scenarios.UNSTARTED_SERVER], 86 | stdout=sys.stdout) 87 | interrupt_and_wait(process) 88 | 89 | def test_running_server(self): 90 | process = subprocess.Popen( 91 | BASE_COMMAND + [_exit_scenarios.RUNNING_SERVER], 92 | stdout=sys.stdout, 93 | stderr=sys.stderr) 94 | wait(process) 95 | 96 | def test_running_server_terminate(self): 97 | process = subprocess.Popen( 98 | BASE_SIGTERM_COMMAND + [_exit_scenarios.RUNNING_SERVER], 99 | stdout=sys.stdout, 100 | stderr=sys.stderr) 101 | interrupt_and_wait(process) 102 | 103 | def test_poll_connectivity_no_server(self): 104 | process = subprocess.Popen( 105 | BASE_COMMAND + [_exit_scenarios.POLL_CONNECTIVITY_NO_SERVER], 106 | stdout=sys.stdout, 107 | stderr=sys.stderr) 108 | wait(process) 109 | 110 | def test_poll_connectivity_no_server_terminate(self): 111 | process = subprocess.Popen( 112 | BASE_SIGTERM_COMMAND + 113 | [_exit_scenarios.POLL_CONNECTIVITY_NO_SERVER], 114 | stdout=sys.stdout, 115 | stderr=sys.stderr) 116 | interrupt_and_wait(process) 117 | 118 | def test_poll_connectivity(self): 119 | process = subprocess.Popen( 120 | BASE_COMMAND + [_exit_scenarios.POLL_CONNECTIVITY], 121 | stdout=sys.stdout, 122 | stderr=sys.stderr) 123 | wait(process) 124 | 125 | def test_poll_connectivity_terminate(self): 126 | process = subprocess.Popen( 127 | BASE_SIGTERM_COMMAND + [_exit_scenarios.POLL_CONNECTIVITY], 128 | stdout=sys.stdout, 129 | stderr=sys.stderr) 130 | interrupt_and_wait(process) 131 | 132 | def test_in_flight_unary_unary_call(self): 133 | process = subprocess.Popen( 134 | BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_UNARY_UNARY_CALL], 135 | stdout=sys.stdout, 136 | stderr=sys.stderr) 137 | interrupt_and_wait(process) 138 | 139 | @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999') 140 | def test_in_flight_unary_stream_call(self): 141 | process = subprocess.Popen( 142 | BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_UNARY_STREAM_CALL], 143 | stdout=sys.stdout, 144 | stderr=sys.stderr) 145 | interrupt_and_wait(process) 146 | 147 | def test_in_flight_stream_unary_call(self): 148 | process = subprocess.Popen( 149 | BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_STREAM_UNARY_CALL], 150 | stdout=sys.stdout, 151 | stderr=sys.stderr) 152 | interrupt_and_wait(process) 153 | 154 | @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999') 155 | def test_in_flight_stream_stream_call(self): 156 | process = subprocess.Popen( 157 | BASE_COMMAND + [_exit_scenarios.IN_FLIGHT_STREAM_STREAM_CALL], 158 | stdout=sys.stdout, 159 | stderr=sys.stderr) 160 | interrupt_and_wait(process) 161 | 162 | @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999') 163 | def test_in_flight_partial_unary_stream_call(self): 164 | process = subprocess.Popen( 165 | BASE_COMMAND + 166 | [_exit_scenarios.IN_FLIGHT_PARTIAL_UNARY_STREAM_CALL], 167 | stdout=sys.stdout, 168 | stderr=sys.stderr) 169 | interrupt_and_wait(process) 170 | 171 | def test_in_flight_partial_stream_unary_call(self): 172 | process = subprocess.Popen( 173 | BASE_COMMAND + 174 | [_exit_scenarios.IN_FLIGHT_PARTIAL_STREAM_UNARY_CALL], 175 | stdout=sys.stdout, 176 | stderr=sys.stderr) 177 | interrupt_and_wait(process) 178 | 179 | @unittest.skipIf(six.PY2, 'https://github.com/grpc/grpc/issues/6999') 180 | def test_in_flight_partial_stream_stream_call(self): 181 | process = subprocess.Popen( 182 | BASE_COMMAND + 183 | [_exit_scenarios.IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL], 184 | stdout=sys.stdout, 185 | stderr=sys.stderr) 186 | interrupt_and_wait(process) 187 | 188 | 189 | if __name__ == '__main__': 190 | unittest.main(verbosity=2) 191 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/_metadata_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests server and client side metadata API.""" 15 | 16 | import unittest 17 | import weakref 18 | 19 | import grpc 20 | from grpc import _channel 21 | import grpc_gcp 22 | 23 | from grpc_gcp_test.unit import test_common 24 | from grpc_gcp_test.unit.framework.common import test_constants 25 | 26 | _CHANNEL_ARGS = (('grpc.primary_user_agent', 'primary-agent'), 27 | ('grpc.secondary_user_agent', 'secondary-agent')) 28 | 29 | _REQUEST = b'\x00\x00\x00' 30 | _RESPONSE = b'\x00\x00\x00' 31 | 32 | _UNARY_UNARY = '/test/UnaryUnary' 33 | _UNARY_STREAM = '/test/UnaryStream' 34 | _STREAM_UNARY = '/test/StreamUnary' 35 | _STREAM_STREAM = '/test/StreamStream' 36 | 37 | _INVOCATION_METADATA = ( 38 | ( 39 | b'invocation-md-key', 40 | u'invocation-md-value', 41 | ), 42 | ( 43 | u'invocation-md-key-bin', 44 | b'\x00\x01', 45 | ), 46 | ) 47 | _EXPECTED_INVOCATION_METADATA = ( 48 | ( 49 | 'invocation-md-key', 50 | 'invocation-md-value', 51 | ), 52 | ( 53 | 'invocation-md-key-bin', 54 | b'\x00\x01', 55 | ), 56 | ) 57 | 58 | _INITIAL_METADATA = ((b'initial-md-key', u'initial-md-value'), 59 | (u'initial-md-key-bin', b'\x00\x02')) 60 | _EXPECTED_INITIAL_METADATA = ( 61 | ( 62 | 'initial-md-key', 63 | 'initial-md-value', 64 | ), 65 | ( 66 | 'initial-md-key-bin', 67 | b'\x00\x02', 68 | ), 69 | ) 70 | 71 | _TRAILING_METADATA = ( 72 | ( 73 | 'server-trailing-md-key', 74 | 'server-trailing-md-value', 75 | ), 76 | ( 77 | 'server-trailing-md-key-bin', 78 | b'\x00\x03', 79 | ), 80 | ) 81 | _EXPECTED_TRAILING_METADATA = _TRAILING_METADATA 82 | 83 | 84 | def _user_agent(metadata): 85 | for key, val in metadata: 86 | if key == 'user-agent': 87 | return val 88 | raise KeyError('No user agent!') 89 | 90 | 91 | def validate_client_metadata(test, servicer_context): 92 | invocation_metadata = servicer_context.invocation_metadata() 93 | test.assertTrue( 94 | test_common.metadata_transmitted(_EXPECTED_INVOCATION_METADATA, 95 | invocation_metadata)) 96 | user_agent = _user_agent(invocation_metadata) 97 | test.assertTrue( 98 | user_agent.startswith('primary-agent ' + _channel._USER_AGENT)) 99 | test.assertTrue(user_agent.endswith('secondary-agent')) 100 | 101 | 102 | def handle_unary_unary(test, request, servicer_context): 103 | validate_client_metadata(test, servicer_context) 104 | servicer_context.send_initial_metadata(_INITIAL_METADATA) 105 | servicer_context.set_trailing_metadata(_TRAILING_METADATA) 106 | return _RESPONSE 107 | 108 | 109 | def handle_unary_stream(test, request, servicer_context): 110 | validate_client_metadata(test, servicer_context) 111 | servicer_context.send_initial_metadata(_INITIAL_METADATA) 112 | servicer_context.set_trailing_metadata(_TRAILING_METADATA) 113 | for _ in range(test_constants.STREAM_LENGTH): 114 | yield _RESPONSE 115 | 116 | 117 | def handle_stream_unary(test, request_iterator, servicer_context): 118 | validate_client_metadata(test, servicer_context) 119 | servicer_context.send_initial_metadata(_INITIAL_METADATA) 120 | servicer_context.set_trailing_metadata(_TRAILING_METADATA) 121 | # TODO(issue:#6891) We should be able to remove this loop 122 | for request in request_iterator: 123 | pass 124 | return _RESPONSE 125 | 126 | 127 | def handle_stream_stream(test, request_iterator, servicer_context): 128 | validate_client_metadata(test, servicer_context) 129 | servicer_context.send_initial_metadata(_INITIAL_METADATA) 130 | servicer_context.set_trailing_metadata(_TRAILING_METADATA) 131 | # TODO(issue:#6891) We should be able to remove this loop, 132 | # and replace with return; yield 133 | for request in request_iterator: 134 | yield _RESPONSE 135 | 136 | 137 | class _MethodHandler(grpc.RpcMethodHandler): 138 | 139 | def __init__(self, test, request_streaming, response_streaming): 140 | self.request_streaming = request_streaming 141 | self.response_streaming = response_streaming 142 | self.request_deserializer = None 143 | self.response_serializer = None 144 | self.unary_unary = None 145 | self.unary_stream = None 146 | self.stream_unary = None 147 | self.stream_stream = None 148 | if self.request_streaming and self.response_streaming: 149 | self.stream_stream = lambda x, y: handle_stream_stream(test, x, y) 150 | elif self.request_streaming: 151 | self.stream_unary = lambda x, y: handle_stream_unary(test, x, y) 152 | elif self.response_streaming: 153 | self.unary_stream = lambda x, y: handle_unary_stream(test, x, y) 154 | else: 155 | self.unary_unary = lambda x, y: handle_unary_unary(test, x, y) 156 | 157 | 158 | class _GenericHandler(grpc.GenericRpcHandler): 159 | 160 | def __init__(self, test): 161 | self._test = test 162 | 163 | def service(self, handler_call_details): 164 | if handler_call_details.method == _UNARY_UNARY: 165 | return _MethodHandler(self._test, False, False) 166 | elif handler_call_details.method == _UNARY_STREAM: 167 | return _MethodHandler(self._test, False, True) 168 | elif handler_call_details.method == _STREAM_UNARY: 169 | return _MethodHandler(self._test, True, False) 170 | elif handler_call_details.method == _STREAM_STREAM: 171 | return _MethodHandler(self._test, True, True) 172 | else: 173 | return None 174 | 175 | 176 | class MetadataTest(unittest.TestCase): 177 | 178 | def setUp(self): 179 | self._server = test_common.test_server() 180 | self._server.add_generic_rpc_handlers((_GenericHandler( 181 | weakref.proxy(self)),)) 182 | port = self._server.add_insecure_port('[::]:0') 183 | self._server.start() 184 | 185 | channel_config = grpc_gcp.api_config_from_text_pb('') 186 | self._channel = grpc_gcp.insecure_channel( 187 | 'localhost:{}'.format(port), 188 | options=_CHANNEL_ARGS + ( 189 | (grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config), 190 | ) 191 | ) 192 | 193 | def tearDown(self): 194 | self._server.stop(0) 195 | 196 | def testUnaryUnary(self): 197 | multi_callable = self._channel.unary_unary(_UNARY_UNARY) 198 | unused_response, call = multi_callable.with_call( 199 | _REQUEST, metadata=_INVOCATION_METADATA) 200 | self.assertTrue( 201 | test_common.metadata_transmitted(_EXPECTED_INITIAL_METADATA, 202 | call.initial_metadata())) 203 | self.assertTrue( 204 | test_common.metadata_transmitted(_EXPECTED_TRAILING_METADATA, 205 | call.trailing_metadata())) 206 | 207 | def testUnaryStream(self): 208 | multi_callable = self._channel.unary_stream(_UNARY_STREAM) 209 | call = multi_callable(_REQUEST, metadata=_INVOCATION_METADATA) 210 | self.assertTrue( 211 | test_common.metadata_transmitted(_EXPECTED_INITIAL_METADATA, 212 | call.initial_metadata())) 213 | for _ in call: 214 | pass 215 | self.assertTrue( 216 | test_common.metadata_transmitted(_EXPECTED_TRAILING_METADATA, 217 | call.trailing_metadata())) 218 | 219 | def testStreamUnary(self): 220 | multi_callable = self._channel.stream_unary(_STREAM_UNARY) 221 | unused_response, call = multi_callable.with_call( 222 | iter([_REQUEST] * test_constants.STREAM_LENGTH), 223 | metadata=_INVOCATION_METADATA) 224 | self.assertTrue( 225 | test_common.metadata_transmitted(_EXPECTED_INITIAL_METADATA, 226 | call.initial_metadata())) 227 | self.assertTrue( 228 | test_common.metadata_transmitted(_EXPECTED_TRAILING_METADATA, 229 | call.trailing_metadata())) 230 | 231 | def testStreamStream(self): 232 | multi_callable = self._channel.stream_stream(_STREAM_STREAM) 233 | call = multi_callable( 234 | iter([_REQUEST] * test_constants.STREAM_LENGTH), 235 | metadata=_INVOCATION_METADATA) 236 | self.assertTrue( 237 | test_common.metadata_transmitted(_EXPECTED_INITIAL_METADATA, 238 | call.initial_metadata())) 239 | for _ in call: 240 | pass 241 | self.assertTrue( 242 | test_common.metadata_transmitted(_EXPECTED_TRAILING_METADATA, 243 | call.trailing_metadata())) 244 | 245 | 246 | if __name__ == '__main__': 247 | unittest.main(verbosity=2) 248 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/_reconnect_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests that a channel will reconnect if a connection is dropped""" 15 | 16 | import socket 17 | import time 18 | import unittest 19 | 20 | import grpc 21 | import grpc_gcp 22 | from grpc.framework.foundation import logging_pool 23 | 24 | from grpc_gcp_test.unit.framework.common import test_constants 25 | 26 | _REQUEST = b'\x00\x00\x00' 27 | _RESPONSE = b'\x00\x00\x01' 28 | 29 | _UNARY_UNARY = '/test/UnaryUnary' 30 | 31 | 32 | def _handle_unary_unary(unused_request, unused_servicer_context): 33 | return _RESPONSE 34 | 35 | 36 | def _get_reuse_socket_option(): 37 | try: 38 | return socket.SO_REUSEPORT 39 | except AttributeError: 40 | # SO_REUSEPORT is unavailable on Windows, but SO_REUSEADDR 41 | # allows forcibly re-binding to a port 42 | return socket.SO_REUSEADDR 43 | 44 | 45 | def _pick_and_bind_port(sock_opt): 46 | # Reserve a port, when we restart the server we want 47 | # to hold onto the port 48 | port = 0 49 | for address_family in (socket.AF_INET6, socket.AF_INET): 50 | try: 51 | s = socket.socket(address_family, socket.SOCK_STREAM) 52 | except socket.error: 53 | continue # this address family is unavailable 54 | s.setsockopt(socket.SOL_SOCKET, sock_opt, 1) 55 | try: 56 | s.bind(('localhost', port)) 57 | # for socket.SOCK_STREAM sockets, it is necessary to call 58 | # listen to get the desired behavior. 59 | s.listen(1) 60 | port = s.getsockname()[1] 61 | except socket.error: 62 | # port was not available on the current address family 63 | # try again 64 | port = 0 65 | break 66 | finally: 67 | s.close() 68 | if s: 69 | return port if port != 0 else _pick_and_bind_port(sock_opt) 70 | else: 71 | return None # no address family was available 72 | 73 | 74 | class ReconnectTest(unittest.TestCase): 75 | 76 | def test_reconnect(self): 77 | server_pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY) 78 | handler = grpc.method_handlers_generic_handler('test', { 79 | 'UnaryUnary': 80 | grpc.unary_unary_rpc_method_handler(_handle_unary_unary) 81 | }) 82 | sock_opt = _get_reuse_socket_option() 83 | port = _pick_and_bind_port(sock_opt) 84 | self.assertIsNotNone(port) 85 | 86 | server = grpc.server(server_pool, (handler,)) 87 | server.add_insecure_port('[::]:{}'.format(port)) 88 | server.start() 89 | 90 | channel_config = grpc_gcp.api_config_from_text_pb('') 91 | channel = grpc_gcp.insecure_channel( 92 | 'localhost:{}'.format(port), 93 | options=((grpc_gcp.API_CONFIG_CHANNEL_ARG, channel_config),) 94 | ) 95 | 96 | multi_callable = channel.unary_unary(_UNARY_UNARY) 97 | self.assertEqual(_RESPONSE, multi_callable(_REQUEST)) 98 | server.stop(None) 99 | # By default, the channel connectivity is checked every 5s 100 | # GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS can be set to change 101 | # this. 102 | time.sleep(5.1) 103 | server = grpc.server(server_pool, (handler,)) 104 | server.add_insecure_port('[::]:{}'.format(port)) 105 | server.start() 106 | self.assertEqual(_RESPONSE, multi_callable(_REQUEST)) 107 | 108 | 109 | if __name__ == '__main__': 110 | unittest.main(verbosity=2) 111 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/_thread_pool.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import threading 16 | from concurrent import futures 17 | 18 | 19 | class RecordingThreadPool(futures.Executor): 20 | """A thread pool that records if used.""" 21 | 22 | def __init__(self, max_workers): 23 | self._tp_executor = futures.ThreadPoolExecutor(max_workers=max_workers) 24 | self._lock = threading.Lock() 25 | self._was_used = False 26 | 27 | def submit(self, fn, *args, **kwargs): 28 | with self._lock: 29 | self._was_used = True 30 | self._tp_executor.submit(fn, *args, **kwargs) 31 | 32 | def was_used(self): 33 | with self._lock: 34 | return self._was_used 35 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/README.md: -------------------------------------------------------------------------------- 1 | These are test keys *NOT* to be used in production. 2 | 3 | The `certificate_hierarchy_1` and `certificate_hierarchy_2` contain 4 | two disjoint but similarly organized certificate hierarchies. Each 5 | contains: 6 | 7 | * The respective root CA cert in `certs/ca.cert.pem` 8 | 9 | * The intermediate CA cert in 10 | `intermediate/certs/intermediate.cert.pem`, signed by the root CA 11 | 12 | * A client cert and a server cert--both signed by the intermediate 13 | CA--in `intermediate/certs/client.cert.pem` and 14 | `intermediate/certs/localhost-1.cert.pem`; the corresponding keys 15 | are in `intermediate/private` 16 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla 5 | Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 6 | YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT 7 | BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7 8 | +L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu 9 | g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd 10 | Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV 11 | HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau 12 | sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m 13 | oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG 14 | Dfcog5wrJytaQ6UA0wE= 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_1/certs/ca.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFZDCCA0ygAwIBAgIJAKfkDFZ6+Ly/MA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV 3 | BAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAOBgNVBAMM 4 | B3Jvb3QgY2EwHhcNMTcxMTAyMDAzNzA1WhcNMzcxMDI4MDAzNzA1WjA/MQswCQYD 5 | VQQGEwJ1czEOMAwGA1UECAwFZHVtbXkxDjAMBgNVBAoMBWR1bW15MRAwDgYDVQQD 6 | DAdyb290IGNhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxlSUuSbi 7 | o66tT2ZCqu9wNqSX8VhAJkmrAT5y6m2V0VlQ8Gz7ddynW5UVSmtvDNTebZ15FrvO 8 | 6Ng7QnwXXNs/dEzl6oMe6AKDZpuWScVkiqH1UYWBkMLRygWCTEYpSTWTpZWk1zxj 9 | DJ2LlIoO1X/ufLyLOfy2a2XEz8ICzJePmqVca6fmfEtCTj1/8FcwCBF6YlUWVzlR 10 | wewjanQo/lorTYbub+Q6LGxPXZ8W0qoKZzLDSD9cnj4pcJzGGFeu9KkNaW4rldZG 11 | t7mTGQqIRc98dDRc9Jb7PqL8tMPLidw1KErUi05ofxggc5vqNnj4xBl6aX6b/EYN 12 | rBLzO2e0FazX6TwNKwwg68vbOanpDq5LVmIUH8bY1zNZ+JPBGO9pXlAA0YwLx86r 13 | R7YhQ431ZpJ2KGnYjVhYnZ2L3NjV3UYX3x5Z3OrDj9hybhucJB48DMQ1+loEabwK 14 | fSUJtcSPc8dCIibxVKidBFgaTPXtHy2MPXuhMhR7PCtMpE7RPUoYmdZLr9FNN1ty 15 | /RAbwBfuhGLbRI2qqJgbOzHJHaOY/FtShfooLz7lt4LIjPTARaNsulG2rbv+m3z9 16 | mhNjL+peV8gni/xyOYYTbdzZagLrtSHeTWsITvmVt0flMHkjHyv35rw23+hBlSjp 17 | 6+S+0MmwuwxqBBccBSlZ9t3Xh1N+vFkb2UkCAwEAAaNjMGEwHQYDVR0OBBYEFJWE 18 | tQwTbTCgZWNN08VSxjdNA0oaMB8GA1UdIwQYMBaAFJWEtQwTbTCgZWNN08VSxjdN 19 | A0oaMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB 20 | CwUAA4ICAQCNjPv/e3ozX1PuN5Tluf0yOmKCxKVCK3Pp98WkDzH4Rp1urEeYrGJL 21 | vBNcl17avOJ0e+zTVYPXFviFbsBsU/zaf+TqEujXabsdL+nvvCJ2mMqYn4wyDFjS 22 | zDNbGH6O0ENZz5NSY0/UGSOHYrYnYB94QRFLbbf0Y3PmBS2eRNjIUnv7ytPZNMi/ 23 | piM+QhPb0Ebyk0rHQZ0RAJaC/wsEtqP8TGV/fx+AzG7zW/zxgPTrgIThy138tLQ+ 24 | xCVDP9H2c17nVP6vjYzKnMZ94uGrGqUzV9vU7EqYl0uZflIf98pLfdKHnQ3heqds 25 | 8KQPNKRxVvcc92qv2pQY951wb1fkhLutjHn7TUvrenyAngz+Vs19NxbqLPys1CTw 26 | iaL7vZ8VE/aEDm1tjt5SLM474tpATjk1+qMRaWnii8J5rTodYHP+Zu2GxyIrMiGq 27 | tfNZMYI0tETK1XmEo75E/3s9pmIeQNGKLFp+qL7xrVyN/2ffNv0at8kkqXluunK9 28 | /Ki0gKYlGFm4Eu8t/nHMqhBx/njYg6pLDuarLW6ftUV7aHd7qKcCWOWqK6gnH/vX 29 | 3Apv31eltZBBVN69p3CFy2oMnjrom2Yn/DUXFwrJLBiNJ1dd1JyDxpqpJ74ZQy+/ 30 | pSRWMTRM5SuC7lYARx5rYPmp6cZJWyWRH/3r7bwS699/W965pa5nug== 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIExzCCAq+gAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx 3 | DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt 4 | ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzcwNloXDTI3MTAzMTAwMzcwNlowPjELMAkG 5 | A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEPMA0GA1UE 6 | AwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwquL6gtP 7 | R7P9xJK76FTj8fI5TSJa3cAMt1p6CmessjHQq7nQ6DWLGVi4XIt9Sc/1C3rXupOe 8 | 90Ok4L0tsuVZH78Wn0EBmBH7S4IbhU9P+aJ9mcigepj1lnxWqoVblgeJYKMOOwAf 9 | pAKUNMWDSm+nCfwE+R5d8d8cfA41Awq1jTRjOVpiJq6aoKfs791a1ZkZde3kFrNV 10 | AVjC06GgA1lZd3sHf94hmLeC+xJztRXVE9e+7dcc7nFDH0t5DIKYBAklsHg77mZa 11 | 3IK4aOZew7Lm6diPoMnAzXh2rWpJU6RrEE29gIkJBsF8CL1Ndg9MzssCg6KBjoai 12 | Vt5dJ+4TSEGCOwIDAQABo4HFMIHCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD 13 | AgWgMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBDbGllbnQgQ2Vy 14 | dGlmaWNhdGUwHQYDVR0OBBYEFPeuKDCswk8jaH9tl6X+uXjo+WM1MB8GA1UdIwQY 15 | MBaAFCoqYgmKh3CUafVp+paXxfz+He+FMA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUE 16 | FjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBADYAp8XS 17 | UjMEpX/zVjRWpAAT4HNEJylCV1QNyhBIAyx38A6xJYuFIx966Htd6W9/Rw4sUY6y 18 | F4pOmnLCRxIPqFzzMYcBHSpynlcu5G7zqIod3wYIk7BNlB0AzkZn6yD8bM1y5Plf 19 | myzQVDEGggrDtaW2EehhNIB+wOmbRGITjIcZUEr8V4BlLXkCqOuxauxl82d5/k2w 20 | LAhjOb9d1VW6RT8+Lcn6drhHZdvtSCe8Z27BcXhaQLL8366mhbigKYJt5adD0KOx 21 | pl0MQcoL1Rth5cJEj+1/lgUaxcnvh7TaIIGEx0h3olQXsTxSTypU/nww2Ic41xdG 22 | xl3xvHsxe20IvOOAMRfS/LPW7MCtQ3k0BqB/rAQvmB0r5YITLlMJuBqg+zjYrG/j 23 | s5szSGAz9r0leFuPraeuZA41d9UBTAJMoVrrQZ4xVHMXQi1oz9E9KlIdbO9+spvC 24 | ulfO+D+Z4a9trYSWhnQL2dSHT0+kHqJ/8GipiUNP/yAC76dRpDVR3xtYNr73iw0j 25 | hyDsVjihTD8JBebs3axnt+Bc+FwoCCd6CVcsggfGUNhu/N5LS78b13PcaRzrUNjU 26 | Eh+8cJvMLst+UQzePlyazzpn7jjN3KsBzWUkbnXCtUs2qRMn8f2gZqliDo7JSFvy 27 | WtBSCYpikOivuJSQUlrHQ8NaXeddyWQzLY79 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFaDCCA1CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCdXMx 3 | DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEQMA4GA1UEAwwHcm9vdCBj 4 | YTAeFw0xNzExMDIwMDM3MDZaFw0yNzEwMzEwMDM3MDZaMEcxCzAJBgNVBAYTAnVz 5 | MQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxGDAWBgNVBAMMD2ludGVy 6 | bWVkaWF0ZSBjYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOOxzve7 7 | CG2P9SvKfXkJVTXkj4y79JSZ77Kud/TiPfDbHTqZWKuLTXkOCkCCxfpuJvWXnnj5 8 | 1AeLCdKx9hEwJQeU23EXDt1K+RsRyl09SXtPNnJnqHD1mUHRQR28vGX5ctrQzK8J 9 | Sa6/mHW4bX8ol100npbgVMDnM4IDfLYcsv4BXMICGkSHOW6Gn0zJaeHzRVPpmnK/ 10 | 0k/GQAcIrU2sZ39kVlVQkWq3HJC28cNL/P04hjh4gAf0evo/k9VrEtxPWYMfiPDt 11 | kOAKueoPv/VTA/zL5t8lyzfhrhxvsJxFg/klapPXK0gLLbhsHyOhnkbrzvmSR4Rw 12 | xubYJ2dDK0DKx+BIZqlFznjP9BvOtvtuVVMyqg9cfgc7J/OjvAguO0f93MLSfIWP 13 | uISqv7Llt/Blvy/xI5owvOKVc/hm3d+5pqjWBC1HkVwo4qugpWmM49dFWl4kc4c7 14 | ayYUjTmcgoj1ZR89w4Off/bPd1A6gXqSkw2VQfgFF+uOos84fP1V+zPWhp3UDY3P 15 | bFeJtuTdv1gR5w1jCIq6xVJ+UsyDZBaYP7yBBRiNzS1/yXJpnXrvHmDfUeQHLBPR 16 | N0nbMjqXJ1dVpZwydiI0Qx9DnJtOaq/spUreXr8+PU2jeQdCCAN21MB1umr2gZBJ 17 | 8MZBStTgE7SDByfGmGfp7B5/s/r4O/rNc4WzAgMBAAGjZjBkMB0GA1UdDgQWBBQq 18 | KmIJiodwlGn1afqWl8X8/h3vhTAfBgNVHSMEGDAWgBSVhLUME20woGVjTdPFUsY3 19 | TQNKGjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG 20 | 9w0BAQsFAAOCAgEAvzLu/Jc8DlfCltVufC54UZ8DVwUfxdGapNBGv4icrs1wMV3S 21 | xqdaLO+vSp9NeEufi724+/hj4URapW9kSt2TMD7BNJ61QSATZFJajxTFgGa0Zz95 22 | RBDw8/b5Arz/2pOF4VX+FJ+wqHvoH/2A0T+fwz8hLORhxZHv/cUN6kif4FKCwryQ 23 | s89e694kXkEiJfquvu7DR9hYCLOJwzMOOJiTnjz3hlQg4WGu7Z8ZvqzCM+how1hr 24 | nYbUx6a+HfoUf79AHJB0N1EsEEetJ+omvTdrrayCvy1bHA3QgHlJ28QZIJ7MzX9E 25 | n11/xQ95iTuSp8iWurzjTjbrm7eHnGUh+5QubYLXOzbqKzNZu72w0uvWv6ptIudU 26 | usttltiwW8H9kP0ArWTcZDPhhPfS9impFlhiPDk1wUv2/7g+Zz1OaOb7IiSH0s8y 27 | FG72AB8ucJ5dNa/2q5dJiM8Gm5CbiVw5RXTBjlfTTkNeM6LBI3dRghpPdU7Kbfhn 28 | xYs9vnRZeRMJHrcodLuwVcpY/gyeJ0k5LD6eIPCJmatkYZ122isYyMX8lL2P5aR+ 29 | 7d2hhqcOCproOrtThjp6nW2jWTB+R/O2+s6mhKSPgfbY2cdky1Y9FSJxSNayb9B8 30 | eQ+A29iOHrGVAA0R/rvw119rLAYxjXzToM28owx7IyXKrBaU4RMv5yokgag= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFFzCCAv+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx 3 | DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt 4 | ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzcwNloXDTI3MTAzMTAwMzcwNlowTTELMAkG 5 | A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEKMAgGA1UE 6 | CwwBMTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 7 | MIIBCgKCAQEArRAy0Nim9P883BAisXdFoKmgHGTtcLH/SzwkkPWTFHz0rHU1Klwz 8 | w8u3OkRyvgoQp7DqkohboNMDwg5VrOOcfKwtM2GZ5jixo+YKvJ25oj8Jfr+40baz 9 | nyWTmOcfoviKrb7u2T9BPEEz5og+lXRDAsTFATGaQDX2LN3Dd9KIw+7sWY+gc3Zi 10 | 13HHaWYhtmfJjzFbH1vDxHKCdSdgtPyEhqcJ4OC6wbgp/mQ01VlPAr08kRfkC8mT 11 | TS7atqc410irKViF3sWi4YNPf7LuBrjo75FIIOp+sQgZE6xwOuZ/9bT2Zx/IUtCC 12 | TqzVgZI0s5NVlINtWR6eyyxQ1uDKTs4xrQIDAQABo4IBBTCCAQEwCQYDVR0TBAIw 13 | ADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2Vu 14 | ZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUDE8pwi7aELJjvyNT 15 | ed81/KIgGfowaAYDVR0jBGEwX4AUKipiCYqHcJRp9Wn6lpfF/P4d74WhQ6RBMD8x 16 | CzAJBgNVBAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAO 17 | BgNVBAMMB3Jvb3QgY2GCAhAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr 18 | BgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEA2cvXxJw120Z9oWXyGwR6CH7TcXoy 19 | 1i77B1M5j0Krvkjh2/MkEU+JxpZcrhAgZODK9wMPeIUIpJNw2t6Hg+vigpInu7pY 20 | MXR4IA5XLnhGV/hueXa0JLia5FG1TISxr4piW6Jd9P2pOt3ECm3Url/F0OeFF/74 21 | jGaAlWkbhqWJ9M7Gd4QP2wUNm0P4CwAqS9DC6dnMz+JXTakEUirOpmq7U8UKT+5N 22 | QS1K4WuH671n4MiYye3+UoRYt4zPjOzN+QxzvAMtkUBspPmWD6txmD5tKUYDECqn 23 | 0sSbY6ytD30OTHIbICFp40arOffmEEJSriL+uQNPPmvqMxX1G2kUFGm15NLPs8Xa 24 | J7ChrAaJzssN5J3myZUbDfCuxmTkWg+hGvGmxLraVNWc3fzKFmdszSkXrGIdf2HR 25 | gZeFI3w6M4Ktx3KctXlsjwqQTYZI/WwLOEpsrHQBPBLQhISyNw4xjZ4MxK8SFZuQ 26 | IiGps/do0eEgeQ+o3gD1dIXt8YxFIxrgk0pzJONpXGgv/cZrukbLNTBdkTSkIwtx 27 | TXKdiJbO17H24MvW+UxFdsIoJXmfQZWdQC3p+Dl0iP+K80aI6WbaysmToHuOi216 28 | e49nmiM72Izul2zmBi7Cq2nRQbHAETsFfqC34FzJlx0aP8WS953IBD0jNi1BB+AX 29 | BxwiZ1rPjeMvekI= 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_1/intermediate/private/client.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAwquL6gtPR7P9xJK76FTj8fI5TSJa3cAMt1p6CmessjHQq7nQ 3 | 6DWLGVi4XIt9Sc/1C3rXupOe90Ok4L0tsuVZH78Wn0EBmBH7S4IbhU9P+aJ9mcig 4 | epj1lnxWqoVblgeJYKMOOwAfpAKUNMWDSm+nCfwE+R5d8d8cfA41Awq1jTRjOVpi 5 | Jq6aoKfs791a1ZkZde3kFrNVAVjC06GgA1lZd3sHf94hmLeC+xJztRXVE9e+7dcc 6 | 7nFDH0t5DIKYBAklsHg77mZa3IK4aOZew7Lm6diPoMnAzXh2rWpJU6RrEE29gIkJ 7 | BsF8CL1Ndg9MzssCg6KBjoaiVt5dJ+4TSEGCOwIDAQABAoIBABECKAFU56JeKYfp 8 | Qh20fQ4Amd0RaVsCkpnaf9s037PaAl9eptADDZozVDhRv6qZTtGn8/1LNJJqCJfS 9 | L5H30+egLHvRlDATMh+QyJLHMTegaNTs4IiVoK97QZ84c54SHoCg/ndNNXaA+y35 10 | K9VvF+sZZ93UN2UQl06Hdz5Cy0YA7L5HIIH3Ezk0ArAw4AarLil5mv4yEz2ApZhm 11 | Tw4I4yNfxB7tZeP+ekNg0XXRL1quA0tGblp+A5fAFfVMDplqqB2d3/KxPR9FSEOi 12 | 4PzBZ5Mq2wQBPIaNog5um9qkw6VKxjl5sQGhP1GGTA8iZqR9iM2+xh57xdCZm3g3 13 | jcr+aPECgYEA42mXTsF/4oBQtU6hh/sOCMWHhxAPstKpQHFMKGYLHKEJ/V1qq0Sd 14 | d0kswAYCmH5G9ookzu5p7pNf0hUUHO5EwelpSZ3FEmtIM+oBwSnDk3vGuadYXN5X 15 | fPuVUla65B1F9SSwapYNBUAiRgrY69Knca2rkTSdcZQaBuWmo684UQcCgYEA2yRE 16 | P23I/9N6AVhKB/zTRtil1AxnTW8o+j7AE4q1o+xly7DS7DT34INaLKLiuG6ylV1F 17 | UoTiqmWqH3A7m3o3Id2AnVf/oDoKV78LCXRF3dJJWvzrPdob2fLlwyjgqXYvmD3O 18 | UH/OFY2blYcAHOYib1Y1AAhHPlXiHA52BYZtnC0CgYAVjjitWmII0ijURrPA8+cM 19 | pcyG3NrgFF++n/6cBbAf8pPD1Er8GPDkEaeQPAGa+r03OTjr9GVOG+IFQ8I4S81w 20 | o/M66x129XxOj2vDJ3ZGUIExr88MXnbkfeRVfasRXET5S5T9RWPOj5mwEe8lyz3b 21 | 5J5SkS4rSeJ9rN7yvPUVmQKBgAvrrB67LRzldxSNpfFLSn7nGBYx2oi2zEbYlQA7 22 | ImhZWqw64S5iLz2yR3x4G9cmhmZjnXrAqcfVIez14PgzLL6V2wI0ID6qCZf+V25b 23 | OdW4M69UZMOHks5HTUJRfe8Z87rXWdq9KQu5GUaIAnSP/D2MNfPbf2yfpV4bV0Yz 24 | qtC9AoGAD3/XXaeGCdV5DPomEmehp84JXU2q/YECRvph46tr4jArG67PCvx2m84B 25 | +W6my4Yi7QJcW4gC0gsdAuxbJl4Y7MCZBnTtNIRCRnHEIciKITJ/+brFln5QUgyn 26 | WnXEPN8q7VjSVXGrljFuLWkzi2Vh8iZDgourNfW+iYDGCJjx1H0= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEArRAy0Nim9P883BAisXdFoKmgHGTtcLH/SzwkkPWTFHz0rHU1 3 | Klwzw8u3OkRyvgoQp7DqkohboNMDwg5VrOOcfKwtM2GZ5jixo+YKvJ25oj8Jfr+4 4 | 0baznyWTmOcfoviKrb7u2T9BPEEz5og+lXRDAsTFATGaQDX2LN3Dd9KIw+7sWY+g 5 | c3Zi13HHaWYhtmfJjzFbH1vDxHKCdSdgtPyEhqcJ4OC6wbgp/mQ01VlPAr08kRfk 6 | C8mTTS7atqc410irKViF3sWi4YNPf7LuBrjo75FIIOp+sQgZE6xwOuZ/9bT2Zx/I 7 | UtCCTqzVgZI0s5NVlINtWR6eyyxQ1uDKTs4xrQIDAQABAoIBAC56mDswxH4uAmlT 8 | yA2Da+a/R6n4jTBkDZ1mFKf93Dd3a7rZa6Lpylk+YAI9GdfiGiD/SbB7AKjLo0m9 9 | 0dKx+ngdQbJ39v42obbT9HQ9o/poFaO91+QyvkDytZYuFHgPaidJjRo5e8qz9D1o 10 | v+4hoFGhCQvOB5BRLcFU+cc3etWr5t61sNL/kKCWEDd+MWWsOCHpdhEoWC+o25pC 11 | bhD3FG5xoz+8zL7WdNfke/4twfKoBJ/kq89bfIkl8eKpg387WBQY44RJF7/zVr7a 12 | 9dsUuW2y/wVXslCHChjSrxhRlOyy5ssv3EgKh8gPkZ+oeKuONqAGw27nyKyvpjxS 13 | i62K+WECgYEA4oKpIS2D77RCC6rpYIK6KYfbUcNSOtHFvcbf0RH9Oi8vSRYum2ZA 14 | /ITdWSFgWkhT6iOSPuvZlu/EvueWDgNgW1ZVsTMFeapz1+Jwk7JRoBKF1dUEwELh 15 | jdAswdh0MLbgBYs6NXtVVkeK2ocgZtosmt1PUktl566NlyIyhOjH6vkCgYEAw5g0 16 | cteTpz+noKsfWcbnjyesuQy0caICfZIE01nKv9rKTF8BtCO6Qxj10iM2o00jW7Vl 17 | tZa/igjuqvozXAHBI3xegtrWV05urkjj3FB/Pyuqsx3wxhAdSNchQjdTjwUBQEzp 18 | 3ztGSlDTRPpijnpW28lg8Kkr3weryaHvl0xM1VUCgYBqnTN8QU8rgT3g/gYw/fcf 19 | 2ylY98V5mAkqBTSN1JjLTTBFh2JSlLOb5/HDpRkUBZ0xxKJuaVaWW67QaHLRj7dH 20 | 5oAZErnOBXPXNmbkrfcLkAxclJJS6Gf/9u9KIla2Iy2YjmrMh4uoO65Yo2eV4bVD 21 | A031nzWM8jUE4PzEYEjRCQKBgHDdTj6KiQg0Yg0DUabjcNEZasCpRSJhAyDkdmZi 22 | 5OzKWnuxQvFowF1hdM/aQ/f9Vg7gYJ1lLIeBWf9NOv+3f3RzmrHVh2N/vbxSETIb 23 | PSH9l5WeDEauG8fhY66q8EuR7sPk3ftTX98YPqEJ/n8Ktz5COO8GH2umKInEKNXc 24 | UGW1AoGAfENy7vInNv0tzFWPSYdFgesvzo7e8mXyVO8hCyWvY3rxW2on7qfLF3Z9 25 | fHjd7P9gULja0n1kvmxwUC3u20RrvpY59F4hfi+ji2EiubS9Yhszd2e1CLeRMkln 26 | ojDjnflN32ZbWVHX/i6g3Dabq9JOD0FsOaOlriLMuofdA6jTUFE= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_2/certs/ca.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFZDCCA0ygAwIBAgIJALhSfZ8i0rWTMA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV 3 | BAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAOBgNVBAMM 4 | B3Jvb3QgY2EwHhcNMTcxMTAyMDAzNzU4WhcNMzcxMDI4MDAzNzU4WjA/MQswCQYD 5 | VQQGEwJ1czEOMAwGA1UECAwFZHVtbXkxDjAMBgNVBAoMBWR1bW15MRAwDgYDVQQD 6 | DAdyb290IGNhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArHaQ3uyp 7 | wVaVPZDYvy/EJbnP7KbZNPBvKpQCDEqg9B2TPaC8WVjiqT6I88c+KcGuB8nADJdk 8 | o10iZC5AwbrXK4ELSCOvBpxYI5sjoFcv3lZ/oe4e650QO2L/ADmtwLaLYK6rZkwW 9 | Sd90yCGF7ZOTZTJZDwmWEl+ohi+2ow6sRMHKcSKUNfx9G5BB7TOzoqUxqH+moEds 10 | YpjVMEcKzQi2FmbRd+8Dlg2eGqA2V4faprGQwoYz8NqJZGa/KPpRvXE2VjSTDN6b 11 | rJ7mmui6eYN53mZEBRYogyoQHdFXhK02FgyoPEgR/wQlLLbQ+xxOcv02YsOljtza 12 | hl5LjeNUYPMjyhef0QpONp+5NcFhZf38DsSq5EWZLLxPScxwl0lBQkJTjo5ARuFl 13 | Mrv50RYrLwv4ImsiO2ftE7gAX4vNsgcixnCHd6rNzoGimf1+DSvDVJ9ujWo7HPN3 14 | 7ONuoyjsU4mUJJpYXs8zHx5WSxaYiPJRcmG3LjcU5/A+Fs7bkqSrlEjJsG29xDrO 15 | vKR7hH+m6MwcIcXSh9wjjAIvHxAALdU9xaYE3hmVkoxew1mRBsYq34h2vpwGOY5r 16 | 0njRQyGGZnVa8qkQd6P3U5fcvLOM8v9QImZqRDS2jAGZXYruo/RIgJpklVX7ZY0+ 17 | CnGdz4YxgLyOBJCDu3aEgL1oON3mg2SsrVMCAwEAAaNjMGEwHQYDVR0OBBYEFOBO 18 | 9R6yEY6KOE+aSClwD2BQtWXKMB8GA1UdIwQYMBaAFOBO9R6yEY6KOE+aSClwD2BQ 19 | tWXKMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB 20 | CwUAA4ICAQBElio7lLZ2eNt0tDPVSkChksc0IJ2/DpjYjqJzqxF/jZ2oToaAn2Er 21 | 9iHl8ggTLB5WrTQxO1ev7qOwQsk9hrgvZ+EQCBTbyDyfNwVjgftH5jdQGdbyrnuJ 22 | yaks1mnd8is5ZZQmmQSd7GVOMTYi/7yH0xI4DHQ386dwnf5eKdoOI7yrVufEMxRx 23 | tB3Wv8KrX4z47zsIO28H/O0T26oUkrd4PEqFwDa5HQr6iG7QQgoGD/DPLgbBudlO 24 | kEos9fmXxiX60RLziKCE/DAxI3YWPvG3WhIWnVj22Oz6apz2pYWpOKwlaihNYrhq 25 | 8xc02vIFwKh+t7D+wF4KHfduyMJ/wKVc5dzpNbTgkZePPKSB7QgbsMeRqbdPoXQF 26 | pMuzfj8VCWzpqBeHqE/adSCZhzeTrnyiYavF4T2vkSC5KJu+MHmbZ3nU9bcnnEy+ 27 | 24oEv9cEAiYNkvftbD+5ByEtkcBB2uT47sbiGrAeco+GxFGUVqi1IjObqrkIrPzV 28 | OjQhTZV6qgYCOuniJiGfoiMeHqdaDybpqo1bIrxSlvGRNcVoOsKt2/KP1DzW4ARZ 29 | hoRvayU2apHz/T5TAailqAW2MsrjGRaVHQTmeZKag8CKtAcjWzui0J2DnfXxUMn8 30 | R3ruFu3xJduOT1VghT9L9udvX9YhPCIKVL9+B5eFX9eMV6N7hUnVug== 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIExzCCAq+gAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx 3 | DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt 4 | ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzgwMFoXDTI3MTAzMTAwMzgwMFowPjELMAkG 5 | A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEPMA0GA1UE 6 | AwwGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyxZFTLqv 7 | Gd9SpFAykyRyLQgHcR5hgD55mz+9fl1OfnMoAc7yTdPVLksDLmeFUlxcvCtLHysJ 8 | klIBX62c6LzbsVcfLg/DPJlQxFnkhJCRKen4fp7x9h62qqJkDFVXsiEFza9L1lsN 9 | 4OwqU8i4RRgZ/xggM/s/wVBtynioeW9QADNmKZ1n6HVKkYwdOynbFSggYfFrL3HL 10 | 54bC9roZUETin0G5wZ9QU+srgivT0a/KC3ourBYHXAI40iHuuOBf3syDVJ6xId/r 11 | 3UO3qkiQ5q7pwglg+8Nx7Q3CFtGZY3ewxSSSDo6BOyweGYMsBaxMO3EyTqecyfXn 12 | 3n4XPqwmDalWYQIDAQABo4HFMIHCMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD 13 | AgWgMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBDbGllbnQgQ2Vy 14 | dGlmaWNhdGUwHQYDVR0OBBYEFP2bodoNQ1tCNEOALPnygGMUfNI+MB8GA1UdIwQY 15 | MBaAFOWzLd7eBJwSNbzRqNsD7MQDCHg/MA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUE 16 | FjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQELBQADggIBAHqUuCLt 17 | olOdR9p/g+KgGPnKuVgMn15Wc2VLCrbbl2P0fuCcNWmnBKqHHgQ1EJEpgnQ2N8m6 18 | tOGucX7IAzlZj36RP4lN3gZqFRSO/OiTOUYpE6Uv1hYRxeMzAYo5sBdCiiypjV9z 19 | H0Ew5NuWRf2/0nFWoywB9ktHcfD8lRFI3o8zUFXmE2JSUPQtKhW3tBkPPjYBlgzD 20 | RD8cq8dVK9P7i3tUENP+MNHJToNLFBqfA9De6bKnhCWHhZkfB0VeeSm4Ja9HkCg/ 21 | DB+PAKMfbLCH5T8gCpEWxNlvj09r9mn37fNjtJPO/goAcNZNO2AURmb/ZQ4ggdry 22 | xb6lm832qplMUMWx//Ore0faEodlEc5d2kEtmcjj79gAypcLmm74q7CPt7xmniyd 23 | XvNT33S2tkh4dSirpCVwq0xyqOP3ZqTsTjudTveTBaTZNhTbCjDbaV7ga47TcH9/ 24 | +OZ3fQKjt2LAC6162wgEFZf10nUgaAXvSlI74gru93vEwWd8Pd3sWfGwuAFX3oKI 25 | JuwL2kxEuoZQmeRiVJu6KQb+Im7d5CIoWViDmfxcSDJfdtSePTqmDURIx87fw14Z 26 | XBWJP4PiK5PRmG/L0cGiDckmDKm/MuD13Z2I/NMl81GNY/q3WY2O7BmddPpAG5dr 27 | sc5hOqA9+jX08XbxKnfBPYllK5skYMkFH5tN 28 | -----END CERTIFICATE----- 29 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFaDCCA1CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCdXMx 3 | DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEQMA4GA1UEAwwHcm9vdCBj 4 | YTAeFw0xNzExMDIwMDM3NTlaFw0yNzEwMzEwMDM3NTlaMEcxCzAJBgNVBAYTAnVz 5 | MQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxGDAWBgNVBAMMD2ludGVy 6 | bWVkaWF0ZSBjYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKuM2iFz 7 | CCKmbs4uLj/8wjtxf+jFmkwzN4Pps4hqJ3NJqWB326LhzWyqM4WiTAJWE02wSJbS 8 | 16RPfbjkVC77OI4+PUdwqxU9vNAP/95w0h6hBSFtkysuT5VVUt5jiY7wnUKgqTCi 9 | MYhYOl+HEP32O4cnxAazkUKKvtyrd4/PvejJ9zz+iYexRnaaGfOFR3co7jQ5QKar 10 | oK4UgJC3mVDZQEMBV0oljkpgVQMAVb4XQU7e+o25JOUkOoK6LdK/b/95khR0jTyD 11 | OBLqd4mCEzxNi+jZ0jLTLPk0c+DnGmRfoNUxnFb40R8QnEIEKwf+JKyl6p89oqOl 12 | pvIZFLZlUWIS4qL+993l1SCqPkWJOAdTg+s/Zh6DeAOhrUn9/wk0aQwZrK7wQQLJ 13 | 4GGhxC/FfuUGsLqZszAVkP8jDEWnzhN2rw3V+C7v6lj4qHhUwqGHuYzCx2Hxl+B8 14 | UyBmZb9gXKVUtAvaZjaL2PDj1ZAxT0KVMlw1ZVrZu45OsHNQuBx/4uIAt6Rga8yt 15 | av1lsq+hFqwI4bU/oZC/oPMacOsB4qEkAA1101WjMc5bg6JOPWobwIqmUXQR1WJE 16 | j30e99HCpk1Cc2+9sUCzNu8KvU5kUY2K90zwqProvj5IfMuDetAVXsEjgW+ZqSho 17 | UMIpJ2M/hzAFl8Z5IRlG+YNfZNXl0FqJ5LzLAgMBAAGjZjBkMB0GA1UdDgQWBBTl 18 | sy3e3gScEjW80ajbA+zEAwh4PzAfBgNVHSMEGDAWgBTgTvUeshGOijhPmkgpcA9g 19 | ULVlyjASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG 20 | 9w0BAQsFAAOCAgEAOS7DtliOUPcVosRfyx9dHcSUc3O+uY8uKjSHhdIrxDJm4lwP 21 | Q6lKg5j8CdMVb+sDQmyBkqQIA/6E13corP6R283jO6W8D4A8kjOiQWpXfjW6OcP3 22 | 4rrDEWhCdeLFSNJIYOFkr2qWJpI/k0VpyDnmY0YluS5WbNjg6zTzGelzhFbV7/S1 23 | cteNAZD0vHD8NmbLVDJjjIY3E/iwzoUzBncLYbDwqyVS1g6utWdSy8LEJxzzqqWJ 24 | pBKlNYILAdh8efBgvotafaxsn2nfjmVmekPn3KcQZuE4Kzv1EQ2PrHpGeJKwh6up 25 | YBL2tav5cAki8bWoGPr2oGmWUf9L2tB57SdWdaY60ifzmQaeGiWPZBSmAz7PRSrz 26 | sR9SMIkBfYVRxXgWwlvr8JYnd2h/Ef5K9fI32nGfje+7/0kPEjNyjehri7sV4Sjt 27 | zzkDiFO+JklrRuLBPMFYOokq6Pcko32FKlE82pe8QkMDS8Sk//9PqCTK9ceB7y6E 28 | NYLNBW/X9SAw/TR5kdRinHHgHyEug7N4+DCU3lU1wl72ZjoiGE7V6c2AssFC2VcE 29 | E+WYxJT1ROJ1/5+U6BKdaIpTwMtRIFRomOEI66iOwOSEwqLIztkqxwpQ7THraWKm 30 | 2W5e54u/efapIDcQFnP3E8r7TD0PdIeU6mD28o0+WiK3uL/OZpvyKaHPeFU= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFFzCCAv+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRzELMAkGA1UEBhMCdXMx 3 | DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEYMBYGA1UEAwwPaW50ZXJt 4 | ZWRpYXRlIGNhMB4XDTE3MTEwMjAwMzc1OVoXDTI3MTAzMTAwMzc1OVowTTELMAkG 5 | A1UEBhMCdXMxDjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQKDAVkdW1teTEKMAgGA1UE 6 | CwwBMTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 7 | MIIBCgKCAQEAycY/7H1xk3/XHZRopULV7YsOzPIrMG25zoACbpDZxjS0I+2r1c7V 8 | wnvE8TszAkloLi+Skku5CYC7IvVEEEuKuIuV+8M48FJEwlCPge8LPiy18C+npCEd 9 | fgDzCV/O9DfJj6UaiCUayVE7UujXoke7AlKQEJcqvnD/CoTv2Y8jV1A6mPf6CTEI 10 | Sl1BMeFSmeFyvZll+xJ8Up1KfQZxKhtpP1s/rp6ZNlqSs1LM5+vcDHHZ6COTbq7t 11 | 2vvcmGDTqeCLsqicBg1kJyMPRtqa0bNPj2bcVtcK0Ndfn6eL2hi+EoBy2nIXi6aG 12 | PpXf85b9bCLd5pZI80nHzFlhdvV+SxqrfwIDAQABo4IBBTCCAQEwCQYDVR0TBAIw 13 | ADARBglghkgBhvhCAQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2Vu 14 | ZXJhdGVkIFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUoYjECaDz/ZELru/r 15 | jfTB1ShlVrAwaAYDVR0jBGEwX4AU5bMt3t4EnBI1vNGo2wPsxAMIeD+hQ6RBMD8x 16 | CzAJBgNVBAYTAnVzMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UECgwFZHVtbXkxEDAO 17 | BgNVBAMMB3Jvb3QgY2GCAhAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr 18 | BgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEAiiR2knMMym4O+3fD1KlYSnc2UR3v 19 | 0FlRVAsr8wvTlVjJhx7DbRusBNJHWX66mUgK9x5OLnhyvyqlFVhR9AwlnxgfLWz9 20 | nnACeXzcjQZnKWFQWu8bJSC6Ene6rd1g2acK6SOjxavVbj7JVFnmlHF/naZUzvMl 21 | mJivYta4k7ob8UcX0I5TlJpzglU3UHyyJd5d9zhbF8wqbBq63zR2ovWci4pYCg+F 22 | jYcTGYVZJti3SHO+9/EqTC9x2KDNs3o0+rreJ3GuoonkInKZMQQZJQ6qILvkxlhT 23 | jyU5xlcaJ+0tSaiFK3eF0nXIpFYdZbIHYPCdLjh9AZ2dkFcAgSa/L8+tsVt60k8D 24 | HTO0Hz6dW5D2ckeebZvz5LACMN89gVzrc/rVkeg7QmpSbjkTSLC2KJS53hJzWcEI 25 | 3KB73B9iY+ZYytcYBTYLizsAxd5g7j9z8UXrmVQ4mWbh2+xKiG+9aVOzCZ09AYi6 26 | WVK2aRcMQshgkkqPOloN9OeQNCE8Exf7N/zHsBhygorJXoD/PFgnV1VZm8xkOdiJ 27 | zTb3bpGdmL5+bzzS6wP8Q7pGZGYdlnB7JNO8oMYPPtzX8OOx92BTkPnqJnnRWTpR 28 | SjMEEdQe8K7iXxejQkjaAq5BlwaAOjCjPTqYomECcYjC0WaXsmrPcnZwSqpnHZZ2 29 | OiINYJub5cvBLNo= 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_2/intermediate/private/client.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAyxZFTLqvGd9SpFAykyRyLQgHcR5hgD55mz+9fl1OfnMoAc7y 3 | TdPVLksDLmeFUlxcvCtLHysJklIBX62c6LzbsVcfLg/DPJlQxFnkhJCRKen4fp7x 4 | 9h62qqJkDFVXsiEFza9L1lsN4OwqU8i4RRgZ/xggM/s/wVBtynioeW9QADNmKZ1n 5 | 6HVKkYwdOynbFSggYfFrL3HL54bC9roZUETin0G5wZ9QU+srgivT0a/KC3ourBYH 6 | XAI40iHuuOBf3syDVJ6xId/r3UO3qkiQ5q7pwglg+8Nx7Q3CFtGZY3ewxSSSDo6B 7 | OyweGYMsBaxMO3EyTqecyfXn3n4XPqwmDalWYQIDAQABAoIBAFhIOR3OtVlw3BLz 8 | jdiq6jsrF1kUFNxTzDcxsSUiWIHde1G17Vzpre0uzJY6iBkyb1mZFFHbOpDxtwkp 9 | hmEh3/qqXbJ/RaatGxAP56e81G28+LnKTHJqDYwFhapa2wFjG4u7HSN0d4cEAq5j 10 | Pb9DZ+GdUjpmiON3HBL8+ne3bLZ42uI+DSVe8d3irbqg2rqsiANf0gdimMW4nuI4 11 | rVxf8HrY43PdQn/Vby+7qLRE3tmIlpbTqJGRtWRjdeBBI91APCrRljjXrKqT6Zpa 12 | E6Daz3YIQvXkIT0q+WkeN1VmQbtRnk7kRsPNp15kSwpHfmv6o/vkO9OUb1n71P2F 13 | wnB0WDECgYEA8iltnKxXnjqwZ/vzIWzcd94j+mdZg/K2/JCOqjwMvpSGCvx2zUmq 14 | Y2nxO2K85AVeOm/Yt87SMODB6AQ9CsrVGEUAzzacvCJDb8oUhaOL5gypnyvZiGCy 15 | snzXfgB+v/xuGekIjs2y7E8h3GG40j0aNQnUY1Fuc6iaeJG4BtjkuQUCgYEA1rE4 16 | DrTSsUh3hLYQusIHZR8Lecrrd4QUZSMKLkWjobiSTw3m4mglx1s2G4eZ3WuzOyFq 17 | Dp3/b3yfT8prdPBGA6shHNFf+1TO1q1/pIt15dc3sFwxMkuunai8N4QZJRqZLbYq 18 | FkNFkZ20hFHcH/NHDsAsRL/0tJdEmJ2ruP+Qdq0CgYBsdPGKwgVb8J0hdU4nIkJ7 19 | zRoABFmrJwGdjIDY7Zwnnw2JzhjHSL7vV3ubRVWkKmNReNZvPEoXahJuf7d3JfDa 20 | tczvAV6hRBc/8hnO4Li/h9xQVatP0T83gYJiBIbAJaaKJDyY+Lex7p8TvRCx2Hvs 21 | VUKyWL5HPrQwW9M3/dwyoQKBgQCNQoPA4Wcz8Jt7PZQaXaoh9eBGHab6t3P366s6 22 | MOXudZQG4f3FgINC/ZfHW1x43PFL+btfrMOyJkxoYqZ7hdB7f3DFFlpR80Y46GVw 23 | 7bYAKbBhoPdZwYQ+BhT5bjhhOnQJKK/egBrZKevpmDb+6sIZSYaXIbovzMv8otmn 24 | WrhB7QKBgAdl+KYBQULCUBp8qCQH5sAQoWErpyuD2FNN6LGknpPqn4DdujvwEP0Z 25 | OSvbauLkI0Qc9/MezKPTeYXlFqdbpItwyySJsUkiI3HhVYlBgDkZ7xb6uHIH5E6I 26 | bKgIW5JEf5I7Eu1iurORkXxCCGMkiQmEs4X5kSXXRYgXfNgAD0FX 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAycY/7H1xk3/XHZRopULV7YsOzPIrMG25zoACbpDZxjS0I+2r 3 | 1c7VwnvE8TszAkloLi+Skku5CYC7IvVEEEuKuIuV+8M48FJEwlCPge8LPiy18C+n 4 | pCEdfgDzCV/O9DfJj6UaiCUayVE7UujXoke7AlKQEJcqvnD/CoTv2Y8jV1A6mPf6 5 | CTEISl1BMeFSmeFyvZll+xJ8Up1KfQZxKhtpP1s/rp6ZNlqSs1LM5+vcDHHZ6COT 6 | bq7t2vvcmGDTqeCLsqicBg1kJyMPRtqa0bNPj2bcVtcK0Ndfn6eL2hi+EoBy2nIX 7 | i6aGPpXf85b9bCLd5pZI80nHzFlhdvV+SxqrfwIDAQABAoIBAQC022161aoTEtjH 8 | m7n8v56vUCCRFVQfEYsljFohrtZ0sdLyDVwjxkSWEYiizXRYTWIDXALd/N+7o9aZ 9 | bAx5Kq0J45wpUYBc8PDO15T6W0DRlxPxWVXDaSddRQ6TTXxcLREPH2dbtx5+asBo 10 | /Woi/Haki0q0hDr8/p2sWSH/+SwtWpOezGVlrWrkMeIhlBwHZfdHVoZvSx65Uv7x 11 | WU07vsjrbXNDwf+2fmklAQrzhedCeh8loGyjtN3cfrTjrE1zqpEsHnlZcJxe6sRB 12 | 1nOqpoUnpZXklDDIYC8EmeubmDJ0jnXOQCDDep3MzVcnZGyF5E/+szaa1NL70Ayj 13 | rbKk1Y3ZAoGBAPy/1ym7Cjl4OGHN2fdkR6iL68ebJozpr+eTSxDNLuBSi5IJxJyG 14 | 1+B4+v1u0RwZ3DjrSQsO5DCbZ+DHU6O/DAJK2CxUED+M+G2kRyffailRQmNzjpRG 15 | 75dIhSkSRYH8vdvEOnGpeQBZwBcCRH/2YUMlZeSfx9fHJhk1nyUxJeHjAoGBAMxe 16 | k+cBb0zYok+Ww1xTwOdq0PwKj0oDsEg8hOdWc8pH0SlOAB4BI5kmfd1JDMHfRc49 17 | 7tpNqjsPrnlb9xd8l0281Lj2NoVSE5KX1JtsOsKecQsvHH5zRk4eJ3h/mNixpjfe 18 | 79Zc/O40T4rWpQRqhat+WHveJC0/ON4AH4uT0BK1AoGBAPcTioCu6YXYsjVaCJPB 19 | IhPwBGOylfL2lxDoel9IVWTRDMOMbPkfEHXNjn6lECJKXW//Af6fZg7mPJwN/wN5 20 | xYGQLNbYrrGRW2HDUBP4YU1WtHGIC3+EAL+BEztdMzmpGuh1YTSvmSvwkMltXA1D 21 | iz0amArw72lOsz29n3+6FfBFAoGAIpRqMC8k9vq80/yth6TAQifnvo3G2v4uyLo8 22 | vqv5IaPvNy70hB8rN9G0gEnI99Dgjdoa3SNBB4dKvUwbTgUN0OB/meBHL13I5Af+ 23 | uGGiu6V1eS/6gUbeAX/Gq/PjF99PQareKAZJ4cBGKTbSayHfBjp1nFflBSbqZ13b 24 | +JEFJvUCgYBOs2J2XXamPbI7gu7B2TE9j/62v0SJyoHq2LHMmYUDRuPdPk3eKCt3 25 | 283w+E8XUIFbctaxsbo8msNjjvV22D/Nci3d87aPe8bn1SVto3GnTuwnOpRq3E+3 26 | wAarqrhiZbGZSCcAkEOk7FlxAwYnCM6paqMxDEMCJ4qChMM42E9ZyQ== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/server1.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD 3 | M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf 4 | 3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY 5 | AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm 6 | V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY 7 | tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p 8 | dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q 9 | K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR 10 | 81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff 11 | DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd 12 | aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2 13 | ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3 14 | XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe 15 | F98XJ7tIFfJq 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/credentials/server1.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET 3 | MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ 4 | dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx 5 | MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV 6 | BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50 7 | ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco 8 | LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg 9 | zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd 10 | 9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw 11 | CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy 12 | em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G 13 | CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6 14 | hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh 15 | y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/framework/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/framework/common/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/framework/common/test_constants.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Constants shared among tests throughout RPC Framework.""" 15 | 16 | # Value for maximum duration in seconds that a test is allowed for its actual 17 | # behavioral logic, excluding all time spent deliberately waiting in the test. 18 | TIME_ALLOWANCE = 10 19 | # Value for maximum duration in seconds of RPCs that may time out as part of a 20 | # test. 21 | SHORT_TIMEOUT = 4 22 | # Absurdly large value for maximum duration in seconds for should-not-time-out 23 | # RPCs made during tests. 24 | LONG_TIMEOUT = 3000 25 | # Values to supply on construction of an object that will service RPCs; these 26 | # should not be used as the actual timeout values of any RPCs made during tests. 27 | DEFAULT_TIMEOUT = 300 28 | MAXIMUM_TIMEOUT = 3600 29 | 30 | # The number of payloads to transmit in streaming tests. 31 | STREAM_LENGTH = 200 32 | 33 | # The size of payloads to transmit in tests. 34 | PAYLOAD_SIZE = 256 * 1024 + 17 35 | 36 | # The concurrency to use in tests of concurrent RPCs that will not create as 37 | # many threads as RPCs. 38 | RPC_CONCURRENCY = 200 39 | 40 | # The concurrency to use in tests of concurrent RPCs that will create as many 41 | # threads as RPCs. 42 | THREAD_CONCURRENCY = 25 43 | 44 | # The size of thread pools to use in tests. 45 | POOL_SIZE = 10 46 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/framework/common/test_control.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Code for instructing systems under test to block or fail.""" 15 | 16 | import abc 17 | import contextlib 18 | import threading 19 | 20 | import six 21 | 22 | 23 | class Defect(Exception): 24 | """Simulates a programming defect raised into in a system under test. 25 | 26 | Use of a standard exception type is too easily misconstrued as an actual 27 | defect in either the test infrastructure or the system under test. 28 | """ 29 | 30 | 31 | class Control(six.with_metaclass(abc.ABCMeta)): 32 | """An object that accepts program control from a system under test. 33 | 34 | Systems under test passed a Control should call its control() method 35 | frequently during execution. The control() method may block, raise an 36 | exception, or do nothing, all according to the enclosing test's desire for 37 | the system under test to simulate hanging, failing, or functioning. 38 | """ 39 | 40 | @abc.abstractmethod 41 | def control(self): 42 | """Potentially does anything.""" 43 | raise NotImplementedError() 44 | 45 | 46 | class PauseFailControl(Control): 47 | """A Control that can be used to pause or fail code under control. 48 | 49 | This object is only safe for use from two threads: one of the system under 50 | test calling control and the other from the test system calling pause, 51 | block_until_paused, and fail. 52 | """ 53 | 54 | def __init__(self): 55 | self._condition = threading.Condition() 56 | self._pause = False 57 | self._paused = False 58 | self._fail = False 59 | 60 | def control(self): 61 | with self._condition: 62 | if self._fail: 63 | raise Defect() 64 | 65 | while self._pause: 66 | self._paused = True 67 | self._condition.notify_all() 68 | self._condition.wait() 69 | self._paused = False 70 | 71 | @contextlib.contextmanager 72 | def pause(self): 73 | """Pauses code under control while controlling code is in context.""" 74 | with self._condition: 75 | self._pause = True 76 | yield 77 | with self._condition: 78 | self._pause = False 79 | self._condition.notify_all() 80 | 81 | def block_until_paused(self): 82 | """Blocks controlling code until code under control is paused. 83 | 84 | May only be called within the context of a pause call. 85 | """ 86 | with self._condition: 87 | while not self._paused: 88 | self._condition.wait() 89 | 90 | @contextlib.contextmanager 91 | def fail(self): 92 | """Fails code under control while controlling code is in context.""" 93 | with self._condition: 94 | self._fail = True 95 | yield 96 | with self._condition: 97 | self._fail = False 98 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/framework/common/test_coverage.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Governs coverage for tests of RPCs throughout RPC Framework.""" 15 | 16 | import abc 17 | 18 | import six 19 | 20 | # This code is designed for use with the unittest module. 21 | # pylint: disable=invalid-name 22 | 23 | 24 | class Coverage(six.with_metaclass(abc.ABCMeta)): 25 | """Specification of test coverage.""" 26 | 27 | @abc.abstractmethod 28 | def testSuccessfulUnaryRequestUnaryResponse(self): 29 | raise NotImplementedError() 30 | 31 | @abc.abstractmethod 32 | def testSuccessfulUnaryRequestStreamResponse(self): 33 | raise NotImplementedError() 34 | 35 | @abc.abstractmethod 36 | def testSuccessfulStreamRequestUnaryResponse(self): 37 | raise NotImplementedError() 38 | 39 | @abc.abstractmethod 40 | def testSuccessfulStreamRequestStreamResponse(self): 41 | raise NotImplementedError() 42 | 43 | @abc.abstractmethod 44 | def testSequentialInvocations(self): 45 | raise NotImplementedError() 46 | 47 | @abc.abstractmethod 48 | def testParallelInvocations(self): 49 | raise NotImplementedError() 50 | 51 | @abc.abstractmethod 52 | def testWaitingForSomeButNotAllParallelInvocations(self): 53 | raise NotImplementedError() 54 | 55 | @abc.abstractmethod 56 | def testCancelledUnaryRequestUnaryResponse(self): 57 | raise NotImplementedError() 58 | 59 | @abc.abstractmethod 60 | def testCancelledUnaryRequestStreamResponse(self): 61 | raise NotImplementedError() 62 | 63 | @abc.abstractmethod 64 | def testCancelledStreamRequestUnaryResponse(self): 65 | raise NotImplementedError() 66 | 67 | @abc.abstractmethod 68 | def testCancelledStreamRequestStreamResponse(self): 69 | raise NotImplementedError() 70 | 71 | @abc.abstractmethod 72 | def testExpiredUnaryRequestUnaryResponse(self): 73 | raise NotImplementedError() 74 | 75 | @abc.abstractmethod 76 | def testExpiredUnaryRequestStreamResponse(self): 77 | raise NotImplementedError() 78 | 79 | @abc.abstractmethod 80 | def testExpiredStreamRequestUnaryResponse(self): 81 | raise NotImplementedError() 82 | 83 | @abc.abstractmethod 84 | def testExpiredStreamRequestStreamResponse(self): 85 | raise NotImplementedError() 86 | 87 | @abc.abstractmethod 88 | def testFailedUnaryRequestUnaryResponse(self): 89 | raise NotImplementedError() 90 | 91 | @abc.abstractmethod 92 | def testFailedUnaryRequestStreamResponse(self): 93 | raise NotImplementedError() 94 | 95 | @abc.abstractmethod 96 | def testFailedStreamRequestUnaryResponse(self): 97 | raise NotImplementedError() 98 | 99 | @abc.abstractmethod 100 | def testFailedStreamRequestStreamResponse(self): 101 | raise NotImplementedError() 102 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/framework/foundation/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/framework/foundation/_logging_pool_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests for grpc.framework.foundation.logging_pool.""" 15 | 16 | import threading 17 | import unittest 18 | 19 | from grpc.framework.foundation import logging_pool 20 | 21 | _POOL_SIZE = 16 22 | 23 | 24 | class _CallableObject(object): 25 | 26 | def __init__(self): 27 | self._lock = threading.Lock() 28 | self._passed_values = [] 29 | 30 | def __call__(self, value): 31 | with self._lock: 32 | self._passed_values.append(value) 33 | 34 | def passed_values(self): 35 | with self._lock: 36 | return tuple(self._passed_values) 37 | 38 | 39 | class LoggingPoolTest(unittest.TestCase): 40 | 41 | def testUpAndDown(self): 42 | pool = logging_pool.pool(_POOL_SIZE) 43 | pool.shutdown(wait=True) 44 | 45 | with logging_pool.pool(_POOL_SIZE) as pool: 46 | self.assertIsNotNone(pool) 47 | 48 | def testTaskExecuted(self): 49 | test_list = [] 50 | 51 | with logging_pool.pool(_POOL_SIZE) as pool: 52 | pool.submit(lambda: test_list.append(object())).result() 53 | 54 | self.assertTrue(test_list) 55 | 56 | def testException(self): 57 | with logging_pool.pool(_POOL_SIZE) as pool: 58 | raised_exception = pool.submit(lambda: 1 / 0).exception() 59 | 60 | self.assertIsNotNone(raised_exception) 61 | 62 | def testCallableObjectExecuted(self): 63 | callable_object = _CallableObject() 64 | passed_object = object() 65 | with logging_pool.pool(_POOL_SIZE) as pool: 66 | future = pool.submit(callable_object, passed_object) 67 | self.assertIsNone(future.result()) 68 | self.assertSequenceEqual((passed_object,), 69 | callable_object.passed_values()) 70 | 71 | 72 | if __name__ == '__main__': 73 | unittest.main(verbosity=2) 74 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/framework/foundation/stream_testing.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Utilities for testing stream-related code.""" 15 | 16 | from grpc.framework.foundation import stream 17 | 18 | 19 | class TestConsumer(stream.Consumer): 20 | """A stream.Consumer instrumented for testing. 21 | 22 | Attributes: 23 | calls: A sequence of value-termination pairs describing the history of calls 24 | made on this object. 25 | """ 26 | 27 | def __init__(self): 28 | self.calls = [] 29 | 30 | def consume(self, value): 31 | """See stream.Consumer.consume for specification.""" 32 | self.calls.append((value, False)) 33 | 34 | def terminate(self): 35 | """See stream.Consumer.terminate for specification.""" 36 | self.calls.append((None, True)) 37 | 38 | def consume_and_terminate(self, value): 39 | """See stream.Consumer.consume_and_terminate for specification.""" 40 | self.calls.append((value, True)) 41 | 42 | def is_legal(self): 43 | """Reports whether or not a legal sequence of calls has been made.""" 44 | terminated = False 45 | for value, terminal in self.calls: 46 | if terminated: 47 | return False 48 | elif terminal: 49 | terminated = True 50 | elif value is None: 51 | return False 52 | else: # pylint: disable=useless-else-on-loop 53 | return True 54 | 55 | def values(self): 56 | """Returns the sequence of values that have been passed to this Consumer.""" 57 | return [value for value, _ in self.calls if value] 58 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/resources.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Constants and functions for data used in testing.""" 15 | 16 | import os 17 | 18 | import pkg_resources 19 | 20 | _ROOT_CERTIFICATES_RESOURCE_PATH = 'credentials/ca.pem' 21 | _PRIVATE_KEY_RESOURCE_PATH = 'credentials/server1.key' 22 | _CERTIFICATE_CHAIN_RESOURCE_PATH = 'credentials/server1.pem' 23 | 24 | 25 | def test_root_certificates(): 26 | return pkg_resources.resource_string(__name__, 27 | _ROOT_CERTIFICATES_RESOURCE_PATH) 28 | 29 | 30 | def private_key(): 31 | return pkg_resources.resource_string(__name__, _PRIVATE_KEY_RESOURCE_PATH) 32 | 33 | 34 | def certificate_chain(): 35 | return pkg_resources.resource_string(__name__, 36 | _CERTIFICATE_CHAIN_RESOURCE_PATH) 37 | 38 | 39 | def cert_hier_1_root_ca_cert(): 40 | return pkg_resources.resource_string( 41 | __name__, 'credentials/certificate_hierarchy_1/certs/ca.cert.pem') 42 | 43 | 44 | def cert_hier_1_intermediate_ca_cert(): 45 | return pkg_resources.resource_string( 46 | __name__, 47 | 'credentials/certificate_hierarchy_1/intermediate/certs/intermediate.cert.pem' 48 | ) 49 | 50 | 51 | def cert_hier_1_client_1_key(): 52 | return pkg_resources.resource_string( 53 | __name__, 54 | 'credentials/certificate_hierarchy_1/intermediate/private/client.key.pem' 55 | ) 56 | 57 | 58 | def cert_hier_1_client_1_cert(): 59 | return pkg_resources.resource_string( 60 | __name__, 61 | 'credentials/certificate_hierarchy_1/intermediate/certs/client.cert.pem' 62 | ) 63 | 64 | 65 | def cert_hier_1_server_1_key(): 66 | return pkg_resources.resource_string( 67 | __name__, 68 | 'credentials/certificate_hierarchy_1/intermediate/private/localhost-1.key.pem' 69 | ) 70 | 71 | 72 | def cert_hier_1_server_1_cert(): 73 | return pkg_resources.resource_string( 74 | __name__, 75 | 'credentials/certificate_hierarchy_1/intermediate/certs/localhost-1.cert.pem' 76 | ) 77 | 78 | 79 | def cert_hier_2_root_ca_cert(): 80 | return pkg_resources.resource_string( 81 | __name__, 'credentials/certificate_hierarchy_2/certs/ca.cert.pem') 82 | 83 | 84 | def cert_hier_2_intermediate_ca_cert(): 85 | return pkg_resources.resource_string( 86 | __name__, 87 | 'credentials/certificate_hierarchy_2/intermediate/certs/intermediate.cert.pem' 88 | ) 89 | 90 | 91 | def cert_hier_2_client_1_key(): 92 | return pkg_resources.resource_string( 93 | __name__, 94 | 'credentials/certificate_hierarchy_2/intermediate/private/client.key.pem' 95 | ) 96 | 97 | 98 | def cert_hier_2_client_1_cert(): 99 | return pkg_resources.resource_string( 100 | __name__, 101 | 'credentials/certificate_hierarchy_2/intermediate/certs/client.cert.pem' 102 | ) 103 | 104 | 105 | def cert_hier_2_server_1_key(): 106 | return pkg_resources.resource_string( 107 | __name__, 108 | 'credentials/certificate_hierarchy_2/intermediate/private/localhost-1.key.pem' 109 | ) 110 | 111 | 112 | def cert_hier_2_server_1_cert(): 113 | return pkg_resources.resource_string( 114 | __name__, 115 | 'credentials/certificate_hierarchy_2/intermediate/certs/localhost-1.cert.pem' 116 | ) 117 | -------------------------------------------------------------------------------- /tests/grpc_gcp_test/unit/test_common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Common code used throughout tests of gRPC.""" 15 | 16 | import collections 17 | 18 | from concurrent import futures 19 | import grpc 20 | import six 21 | 22 | INVOCATION_INITIAL_METADATA = ( 23 | ('0', 'abc'), 24 | ('1', 'def'), 25 | ('2', 'ghi'), 26 | ) 27 | SERVICE_INITIAL_METADATA = ( 28 | ('3', 'jkl'), 29 | ('4', 'mno'), 30 | ('5', 'pqr'), 31 | ) 32 | SERVICE_TERMINAL_METADATA = ( 33 | ('6', 'stu'), 34 | ('7', 'vwx'), 35 | ('8', 'yza'), 36 | ) 37 | DETAILS = 'test details' 38 | 39 | 40 | def metadata_transmitted(original_metadata, transmitted_metadata): 41 | """Judges whether or not metadata was acceptably transmitted. 42 | 43 | gRPC is allowed to insert key-value pairs into the metadata values given by 44 | applications and to reorder key-value pairs with different keys but it is not 45 | allowed to alter existing key-value pairs or to reorder key-value pairs with 46 | the same key. 47 | 48 | Args: 49 | original_metadata: A metadata value used in a test of gRPC. An iterable over 50 | iterables of length 2. 51 | transmitted_metadata: A metadata value corresponding to original_metadata 52 | after having been transmitted via gRPC. An iterable over iterables of 53 | length 2. 54 | 55 | Returns: 56 | A boolean indicating whether transmitted_metadata accurately reflects 57 | original_metadata after having been transmitted via gRPC. 58 | """ 59 | original = collections.defaultdict(list) 60 | for key, value in original_metadata: 61 | original[key].append(value) 62 | transmitted = collections.defaultdict(list) 63 | for key, value in transmitted_metadata: 64 | transmitted[key].append(value) 65 | 66 | for key, values in six.iteritems(original): 67 | transmitted_values = transmitted[key] 68 | transmitted_iterator = iter(transmitted_values) 69 | try: 70 | for value in values: 71 | while True: 72 | transmitted_value = next(transmitted_iterator) 73 | if value == transmitted_value: 74 | break 75 | except StopIteration: 76 | return False 77 | else: 78 | return True 79 | 80 | 81 | def test_secure_channel(target, channel_credentials, server_host_override): 82 | """Creates an insecure Channel to a remote host. 83 | 84 | Args: 85 | host: The name of the remote host to which to connect. 86 | port: The port of the remote host to which to connect. 87 | channel_credentials: The implementations.ChannelCredentials with which to 88 | connect. 89 | server_host_override: The target name used for SSL host name checking. 90 | 91 | Returns: 92 | An implementations.Channel to the remote host through which RPCs may be 93 | conducted. 94 | """ 95 | channel = grpc.secure_channel(target, channel_credentials, (( 96 | 'grpc.ssl_target_name_override', 97 | server_host_override, 98 | ),)) 99 | return channel 100 | 101 | 102 | def test_server(max_workers=10): 103 | """Creates an insecure grpc server. 104 | 105 | These servers have SO_REUSEPORT disabled to prevent cross-talk. 106 | """ 107 | return grpc.server( 108 | futures.ThreadPoolExecutor(max_workers=max_workers), 109 | options=(('grpc.so_reuseport', 0),)) 110 | -------------------------------------------------------------------------------- /tests/integration.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | python -m grpc_gcp_test.integration.spanner_test 4 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | grpcio 2 | grpcio-tools 3 | grpcio-gcp 4 | google-auth 5 | requests -------------------------------------------------------------------------------- /tests/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 gRPC-GCP authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import setuptools 15 | import version 16 | 17 | LICENSE = 'Apache License 2.0' 18 | 19 | CLASSIFIERS = [ 20 | 'Development Status :: 5 - Production/Stable', 21 | 'Programming Language :: Python', 22 | 'Programming Language :: Python :: 2', 23 | 'Programming Language :: Python :: 2.7', 24 | 'Programming Language :: Python :: 3', 25 | 'Programming Language :: Python :: 3.4', 26 | 'Programming Language :: Python :: 3.5', 27 | 'Programming Language :: Python :: 3.6', 28 | 'License :: OSI Approved :: Apache Software License', 29 | ] 30 | 31 | PACKAGE_DATA = { 32 | 'grpc_gcp_test.unit': [ 33 | 'credentials/ca.pem', 34 | 'credentials/server1.key', 35 | 'credentials/server1.pem', 36 | ], 37 | 'grpc_gcp_test.integration': [ 38 | 'spanner.grpc.config', 39 | ], 40 | 'grpc_gcp_test.benchmark': [ 41 | 'spanner.grpc.config', 42 | ], 43 | 'grpc_gcp_test.stress': [ 44 | 'spanner.grpc.config', 45 | ], 46 | } 47 | 48 | setuptools.setup( 49 | name='grpcio-gcp-test', 50 | version=version.GRPC_GCP, 51 | description='gRPC extensions tests for Google Cloud Platform', 52 | author='The gRPC-GCP Authors', 53 | author_email='grpc-io@googlegroups.com', 54 | url='https://grpc.io', 55 | license=LICENSE, 56 | classifiers=CLASSIFIERS, 57 | packages=setuptools.find_packages(), 58 | package_data=PACKAGE_DATA, 59 | include_package_data=True, 60 | install_requires=[ 61 | 'absl-py', 62 | 'grpcio-gcp>={version}'.format(version=version.GRPC_GCP), 63 | 'google-auth', 64 | 'google-cloud-monitoring', 65 | 'requests', 66 | ], 67 | ) 68 | -------------------------------------------------------------------------------- /tests/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | rm -rf google 4 | pip uninstall grpcio-gcp-test -y 5 | pip install -rrequirements.txt 6 | cp -f ../template/version.py version.py 7 | ./codegen.sh 8 | pip install . 9 | -------------------------------------------------------------------------------- /tests/stress.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | python -m grpc_gcp_test.stress.client "$@" -------------------------------------------------------------------------------- /tests/unit.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd "$(dirname "$0")" 3 | python -m unittest discover grpc_gcp_test/unit/ -p "*_test.py" 4 | --------------------------------------------------------------------------------