From 7820b8d088687b5efc1a6b0be3fc62e3ccdbcaf4 Mon Sep 17 00:00:00 2001 From: Clemens Gruber Date: Thu, 11 Oct 2018 17:56:58 +0200 Subject: Fix invalid version .so links in Makefile --- templates/Makefile.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'templates') diff --git a/templates/Makefile.template b/templates/Makefile.template index 0b67416d3e..8bb06176bf 100644 --- a/templates/Makefile.template +++ b/templates/Makefile.template @@ -1370,7 +1370,7 @@ ifeq ($(SYSTEM),MINGW32) $(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/lib${lib.name}$(SHARED_VERSION_${lang_to_var[lib.language]})-dll.a $(prefix)/lib/lib${lib.name}.a else ifneq ($(SYSTEM),Darwin) - $(Q) ln -sf $(SHARED_PREFIX)${lib.name}$(SHARED_VERSION_${lang_to_var[lib.language]}).$(SHARED_EXT_${lang_to_var[lib.language]}) $(prefix)/lib/lib${lib.name}.so.${settings.core_version.major} + $(Q) ln -sf $(SHARED_PREFIX)${lib.name}$(SHARED_VERSION_${lang_to_var[lib.language]}).$(SHARED_EXT_${lang_to_var[lib.language]}) $(prefix)/lib/lib${lib.name}.so.${settings.get(lang_to_var[lib.language].lower() + '_version').major} $(Q) ln -sf $(SHARED_PREFIX)${lib.name}$(SHARED_VERSION_${lang_to_var[lib.language]}).$(SHARED_EXT_${lang_to_var[lib.language]}) $(prefix)/lib/lib${lib.name}.so endif % endif -- cgit v1.2.3 From ace83fa2661009693ebed055f42363931d736c26 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Fri, 26 Oct 2018 14:49:42 -0700 Subject: Change the template of gRPC-C++.podspec as well --- templates/gRPC-C++.podspec.template | 3 +++ 1 file changed, 3 insertions(+) (limited to 'templates') diff --git a/templates/gRPC-C++.podspec.template b/templates/gRPC-C++.podspec.template index cff252b3a9..ed69a1ed4c 100644 --- a/templates/gRPC-C++.podspec.template +++ b/templates/gRPC-C++.podspec.template @@ -177,6 +177,9 @@ s.default_subspecs = 'Interface', 'Implementation' + # Certificates, to be able to establish TLS connections: + s.resource_bundles = { 'gRPCCertificates' => ['etc/roots.pem'] } + s.header_mappings_dir = 'include/grpcpp' s.subspec 'Interface' do |ss| -- cgit v1.2.3 From 4de6dac703fe098481a8f4c91bcb2dae52ae0031 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 30 Oct 2018 21:08:06 +0100 Subject: upgrade bazel in Dockerfile to 0.17.1 --- templates/tools/dockerfile/bazel.include | 7 ++++--- tools/dockerfile/test/bazel/Dockerfile | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'templates') diff --git a/templates/tools/dockerfile/bazel.include b/templates/tools/dockerfile/bazel.include index ee4f9556af..c7714f5663 100644 --- a/templates/tools/dockerfile/bazel.include +++ b/templates/tools/dockerfile/bazel.include @@ -1,5 +1,6 @@ #======================== # Bazel installation -RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" > /etc/apt/sources.list.d/bazel.list -RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add - -RUN apt-get -y update && apt-get -y install bazel=0.15.0 && apt-get clean + +RUN apt-get update && apt-get install -y wget && apt-get clean +RUN wget -q https://github.com/bazelbuild/bazel/releases/download/0.17.1/bazel-0.17.1-linux-x86_64 -O /usr/local/bin/bazel +RUN chmod 755 /usr/local/bin/bazel diff --git a/tools/dockerfile/test/bazel/Dockerfile b/tools/dockerfile/test/bazel/Dockerfile index 4f913dc396..0aa6209f4f 100644 --- a/tools/dockerfile/test/bazel/Dockerfile +++ b/tools/dockerfile/test/bazel/Dockerfile @@ -44,9 +44,10 @@ RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 t #======================== # Bazel installation -RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" > /etc/apt/sources.list.d/bazel.list -RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add - -RUN apt-get -y update && apt-get -y install bazel=0.15.0 && apt-get clean + +RUN apt-get update && apt-get install -y wget && apt-get clean +RUN wget -q https://github.com/bazelbuild/bazel/releases/download/0.17.1/bazel-0.17.1-linux-x86_64 -O /usr/local/bin/bazel +RUN chmod 755 /usr/local/bin/bazel RUN mkdir -p /var/local/jenkins -- cgit v1.2.3 From a01eeb47d7accb7378075e1128854f917292e6cf Mon Sep 17 00:00:00 2001 From: Tommy Chen Date: Mon, 12 Nov 2018 16:56:44 -0800 Subject: re-running tests, squashing commits --- config.m4 | 3 +- src/php/ext/grpc/channel.c | 2 + src/php/ext/grpc/channel.h | 3 ++ src/php/ext/grpc/php_grpc.c | 116 +++++++++++++++++++++++++++++++++++++++++++ templates/config.m4.template | 3 +- 5 files changed, 125 insertions(+), 2 deletions(-) (limited to 'templates') diff --git a/config.m4 b/config.m4 index d0e22a8e43..fadae568ac 100644 --- a/config.m4 +++ b/config.m4 @@ -664,7 +664,8 @@ if test "$PHP_GRPC" != "no"; then third_party/boringssl/third_party/fiat/curve25519.c \ , $ext_shared, , -fvisibility=hidden \ -DOPENSSL_NO_ASM -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN \ - -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0) + -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0 \ + -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1) PHP_ADD_BUILD_DIR($ext_builddir/src/php/ext/grpc) diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c index b17f3d9a61..c06bdea7fe 100644 --- a/src/php/ext/grpc/channel.c +++ b/src/php/ext/grpc/channel.c @@ -393,6 +393,8 @@ PHP_METHOD(Channel, __construct) { channel->wrapper->target = strdup(target); channel->wrapper->args_hashstr = strdup(sha1str); channel->wrapper->creds_hashstr = NULL; + channel->wrapper->creds = creds; + channel->wrapper->args = args; if (creds != NULL && creds->hashstr != NULL) { php_grpc_int creds_hashstr_len = strlen(creds->hashstr); char *channel_creds_hashstr = malloc(creds_hashstr_len + 1); diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h index 27752c9a3f..ce17c4a58a 100644 --- a/src/php/ext/grpc/channel.h +++ b/src/php/ext/grpc/channel.h @@ -19,6 +19,7 @@ #ifndef NET_GRPC_PHP_GRPC_CHANNEL_H_ #define NET_GRPC_PHP_GRPC_CHANNEL_H_ +#include "channel_credentials.h" #include "php_grpc.h" /* Class entry for the PHP Channel class */ @@ -32,6 +33,8 @@ typedef struct _grpc_channel_wrapper { char *creds_hashstr; size_t ref_count; gpr_mu mu; + grpc_channel_args args; + wrapped_grpc_channel_credentials *creds; } grpc_channel_wrapper; /* Wrapper struct for grpc_channel that can be associated with a PHP object */ diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c index fabd98975d..89ee9f7f1f 100644 --- a/src/php/ext/grpc/php_grpc.c +++ b/src/php/ext/grpc/php_grpc.c @@ -26,6 +26,8 @@ #include "call_credentials.h" #include "server_credentials.h" #include "completion_queue.h" +#include +#include ZEND_DECLARE_MODULE_GLOBALS(grpc) static PHP_GINIT_FUNCTION(grpc); @@ -86,6 +88,119 @@ ZEND_GET_MODULE(grpc) } */ /* }}} */ +void create_new_channel( + wrapped_grpc_channel *channel, + char *target, + grpc_channel_args args, + wrapped_grpc_channel_credentials *creds) { + if (creds == NULL) { + channel->wrapper->wrapped = grpc_insecure_channel_create(target, &args, + NULL); + } else { + channel->wrapper->wrapped = + grpc_secure_channel_create(creds->wrapped, target, &args, NULL); + } +} + +void acquire_persistent_locks() { + zval *data; + PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data) + php_grpc_zend_resource *rsrc = + (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data) + if (rsrc == NULL) { + break; + } + channel_persistent_le_t* le = rsrc->ptr; + + gpr_mu_lock(&le->channel->mu); + PHP_GRPC_HASH_FOREACH_END() +} + +void release_persistent_locks() { + zval *data; + PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data) + php_grpc_zend_resource *rsrc = + (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data) + if (rsrc == NULL) { + break; + } + channel_persistent_le_t* le = rsrc->ptr; + + gpr_mu_unlock(&le->channel->mu); + PHP_GRPC_HASH_FOREACH_END() +} + +void destroy_grpc_channels() { + zval *data; + PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data) + php_grpc_zend_resource *rsrc = + (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data) + if (rsrc == NULL) { + break; + } + channel_persistent_le_t* le = rsrc->ptr; + + wrapped_grpc_channel wrapped_channel; + wrapped_channel.wrapper = le->channel; + grpc_channel_wrapper *channel = wrapped_channel.wrapper; + grpc_channel_destroy(channel->wrapped); + PHP_GRPC_HASH_FOREACH_END() +} + +void restart_channels() { + zval *data; + PHP_GRPC_HASH_FOREACH_VAL_START(&grpc_persistent_list, data) + php_grpc_zend_resource *rsrc = + (php_grpc_zend_resource*) PHP_GRPC_HASH_VALPTR_TO_VAL(data) + if (rsrc == NULL) { + break; + } + channel_persistent_le_t* le = rsrc->ptr; + + wrapped_grpc_channel wrapped_channel; + wrapped_channel.wrapper = le->channel; + grpc_channel_wrapper *channel = wrapped_channel.wrapper; + create_new_channel(&wrapped_channel, channel->target, channel->args, + channel->creds); + gpr_mu_unlock(&channel->mu); + PHP_GRPC_HASH_FOREACH_END() +} + +void prefork() { + acquire_persistent_locks(); +} + +void postfork_child() { + // loop through persistant list and destroy all underlying grpc_channel objs + destroy_grpc_channels(); + + // clear completion queue + grpc_php_shutdown_completion_queue(); + + // clean-up grpc_core + grpc_shutdown(); + if (grpc_is_initialized() > 0) { + zend_throw_exception(spl_ce_UnexpectedValueException, + "Oops, failed to shutdown gRPC Core after fork()", + 1 TSRMLS_CC); + } + + // restart grpc_core + grpc_init(); + grpc_php_init_completion_queue(); + + // re-create grpc_channel and point wrapped to it + // unlock wrapped grpc channel mutex + restart_channels(); +} + +void postfork_parent() { + release_persistent_locks(); +} + +void register_fork_handlers() { + pthread_atfork(&prefork, &postfork_parent, &postfork_child); +} /* {{{ PHP_MINIT_FUNCTION */ @@ -265,6 +380,7 @@ PHP_MINFO_FUNCTION(grpc) { PHP_RINIT_FUNCTION(grpc) { if (!GRPC_G(initialized)) { grpc_init(); + register_fork_handlers(); grpc_php_init_completion_queue(TSRMLS_C); GRPC_G(initialized) = 1; } diff --git a/templates/config.m4.template b/templates/config.m4.template index 19f9904911..18378cfc34 100644 --- a/templates/config.m4.template +++ b/templates/config.m4.template @@ -45,7 +45,8 @@ % endfor , $ext_shared, , -fvisibility=hidden ${"\\"} -DOPENSSL_NO_ASM -D_GNU_SOURCE -DWIN32_LEAN_AND_MEAN ${"\\"} - -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0) + -D_HAS_EXCEPTIONS=0 -DNOMINMAX -DGRPC_ARES=0 ${"\\"} + -DGRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK=1) PHP_ADD_BUILD_DIR($ext_builddir/src/php/ext/grpc) <% -- cgit v1.2.3 From ce4a8a66eeb25b34cdd63305877acb6c40371a8d Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Mon, 19 Nov 2018 10:37:39 -0800 Subject: Boost C++ version for 1.17.0 --- gRPC-C++.podspec | 2 +- templates/gRPC-C++.podspec.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'templates') diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 5fd3945efb..fae5ce4a6e 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.name = 'gRPC-C++' # TODO (mxyan): use version that match gRPC version when pod is stabilized # version = '1.17.0-dev' - version = '0.0.3' + version = '0.0.4' s.version = version s.summary = 'gRPC C++ library' s.homepage = 'https://grpc.io' diff --git a/templates/gRPC-C++.podspec.template b/templates/gRPC-C++.podspec.template index ed69a1ed4c..ab330415af 100644 --- a/templates/gRPC-C++.podspec.template +++ b/templates/gRPC-C++.podspec.template @@ -132,7 +132,7 @@ s.name = 'gRPC-C++' # TODO (mxyan): use version that match gRPC version when pod is stabilized # version = '${settings.version}' - version = '0.0.3' + version = '0.0.4' s.version = version s.summary = 'gRPC C++ library' s.homepage = 'https://grpc.io' -- cgit v1.2.3 From 667a17c1f36847c74d95e5edb91f6704a50e033f Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 19 Nov 2018 22:15:22 +0100 Subject: split up coverage runs by language --- .../test/multilang_jessie_x64/Dockerfile.template | 41 ----- .../test/multilang_jessie_x64/Dockerfile | 170 --------------------- tools/internal_ci/linux/grpc_coverage.sh | 20 ++- tools/run_tests/run_tests.py | 18 +-- 4 files changed, 18 insertions(+), 231 deletions(-) delete mode 100644 templates/tools/dockerfile/test/multilang_jessie_x64/Dockerfile.template delete mode 100644 tools/dockerfile/test/multilang_jessie_x64/Dockerfile (limited to 'templates') diff --git a/templates/tools/dockerfile/test/multilang_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/multilang_jessie_x64/Dockerfile.template deleted file mode 100644 index ac687b710f..0000000000 --- a/templates/tools/dockerfile/test/multilang_jessie_x64/Dockerfile.template +++ /dev/null @@ -1,41 +0,0 @@ -%YAML 1.2 ---- | - # Copyright 2016 gRPC authors. - # - # Licensed under the Apache License, Version 2.0 (the "License"); - # you may not use this file except in compliance with the License. - # You may obtain a copy of the License at - # - # http://www.apache.org/licenses/LICENSE-2.0 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, - # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - # See the License for the specific language governing permissions and - # limitations under the License. - - FROM debian:jessie - - <%include file="../../apt_get_basic.include"/> - <%include file="../../gcp_api_libraries.include"/> - <%include file="../../csharp_deps.include"/> - <%include file="../../csharp_dotnetcli_deps.include"/> - <%include file="../../cxx_deps.include"/> - <%include file="../../node_deps.include"/> - <%include file="../../php_deps.include"/> - <%include file="../../ruby_deps.include"/> - <%include file="../../python_deps.include"/> - # Install pip and virtualenv for Python 3.4 - RUN curl https://bootstrap.pypa.io/get-pip.py | python3.4 - RUN python3.4 -m pip install virtualenv - - # Install coverage for Python test coverage reporting - RUN pip install coverage - ENV PATH ~/.local/bin:$PATH - - # Install Mako to generate files in grpc/grpc-node - RUN pip install Mako - - <%include file="../../run_tests_addons.include"/> - # Define the default command. - CMD ["bash"] diff --git a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile b/tools/dockerfile/test/multilang_jessie_x64/Dockerfile deleted file mode 100644 index 3c95554b02..0000000000 --- a/tools/dockerfile/test/multilang_jessie_x64/Dockerfile +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright 2016 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM debian:jessie - -# Install Git and basic packages. -RUN apt-get update && apt-get install -y \ - autoconf \ - autotools-dev \ - build-essential \ - bzip2 \ - ccache \ - curl \ - dnsutils \ - gcc \ - gcc-multilib \ - git \ - golang \ - gyp \ - lcov \ - libc6 \ - libc6-dbg \ - libc6-dev \ - libgtest-dev \ - libtool \ - make \ - perl \ - strace \ - python-dev \ - python-setuptools \ - python-yaml \ - telnet \ - unzip \ - wget \ - zip && apt-get clean - -#================ -# Build profiling -RUN apt-get update && apt-get install -y time && apt-get clean - -# Google Cloud platform API libraries -RUN apt-get update && apt-get install -y python-pip && apt-get clean -RUN pip install --upgrade google-api-python-client oauth2client - -#================ -# C# dependencies - -# Update to a newer version of mono -RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF -RUN echo "deb http://download.mono-project.com/repo/debian jessie main" | tee /etc/apt/sources.list.d/mono-official.list -RUN echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list -RUN echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list - -# Install dependencies -RUN apt-get update && apt-get -y dist-upgrade && apt-get install -y \ - mono-devel \ - ca-certificates-mono \ - nuget \ - && apt-get clean - -RUN nuget update -self - -#================= -# Use cmake 3.6 from jessie-backports -# needed to build grpc_csharp_ext with cmake - -RUN echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/jessie-backports.list -RUN apt-get update && apt-get install -t jessie-backports -y cmake && apt-get clean - -# Install dotnet SDK based on https://www.microsoft.com/net/core#debian -RUN apt-get update && apt-get install -y curl libunwind8 gettext -# dotnet-dev-1.0.0-preview2-003131 -RUN curl -sSL -o dotnet100.tar.gz https://go.microsoft.com/fwlink/?LinkID=827530 -RUN mkdir -p /opt/dotnet && tar zxf dotnet100.tar.gz -C /opt/dotnet -# dotnet-dev-1.0.1 -RUN curl -sSL -o dotnet101.tar.gz https://go.microsoft.com/fwlink/?LinkID=843453 -RUN mkdir -p /opt/dotnet && tar zxf dotnet101.tar.gz -C /opt/dotnet -RUN ln -s /opt/dotnet/dotnet /usr/local/bin - -# Trigger the population of the local package cache -ENV NUGET_XMLDOC_MODE skip -RUN mkdir warmup \ - && cd warmup \ - && dotnet new \ - && cd .. \ - && rm -rf warmup - -#================= -# C++ dependencies -RUN apt-get update && apt-get -y install libgflags-dev libgtest-dev libc++-dev clang && apt-get clean - -#================== -# Node dependencies - -# Install nvm -RUN touch .profile -RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.25.4/install.sh | bash -# Install all versions of node that we want to test -RUN /bin/bash -l -c "nvm install 4 && npm config set cache /tmp/npm-cache" -RUN /bin/bash -l -c "nvm install 5 && npm config set cache /tmp/npm-cache" -RUN /bin/bash -l -c "nvm install 6 && npm config set cache /tmp/npm-cache" -RUN /bin/bash -l -c "nvm install 8 && npm config set cache /tmp/npm-cache" -RUN /bin/bash -l -c "nvm install 9 && npm config set cache /tmp/npm-cache" -RUN /bin/bash -l -c "nvm install 10 && npm config set cache /tmp/npm-cache" -RUN /bin/bash -l -c "nvm alias default 10" -#================= -# PHP dependencies - -# Install dependencies - -RUN apt-get update && apt-get install -y \ - git php5 php5-dev phpunit unzip - -#================== -# Ruby dependencies - -# Install rvm -RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 -RUN \curl -sSL https://get.rvm.io | bash -s stable - -# Install Ruby 2.1 -RUN /bin/bash -l -c "rvm install ruby-2.1" -RUN /bin/bash -l -c "rvm use --default ruby-2.1" -RUN /bin/bash -l -c "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc" -RUN /bin/bash -l -c "echo 'export PATH=/usr/local/rvm/bin:$PATH' >> ~/.bashrc" -RUN /bin/bash -l -c "echo 'rvm --default use ruby-2.1' >> ~/.bashrc" -RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc" - -#==================== -# Python dependencies - -# Install dependencies - -RUN apt-get update && apt-get install -y \ - python-all-dev \ - python3-all-dev \ - python-pip - -# Install Python packages from PyPI -RUN pip install --upgrade pip==10.0.1 -RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 - -# Install pip and virtualenv for Python 3.4 -RUN curl https://bootstrap.pypa.io/get-pip.py | python3.4 -RUN python3.4 -m pip install virtualenv - -# Install coverage for Python test coverage reporting -RUN pip install coverage -ENV PATH ~/.local/bin:$PATH - -# Install Mako to generate files in grpc/grpc-node -RUN pip install Mako - - -RUN mkdir /var/local/jenkins - -# Define the default command. -CMD ["bash"] diff --git a/tools/internal_ci/linux/grpc_coverage.sh b/tools/internal_ci/linux/grpc_coverage.sh index 97166372ab..ae82019337 100755 --- a/tools/internal_ci/linux/grpc_coverage.sh +++ b/tools/internal_ci/linux/grpc_coverage.sh @@ -21,12 +21,20 @@ cd $(dirname $0)/../../.. source tools/internal_ci/helper_scripts/prepare_build_linux_rc python tools/run_tests/run_tests.py \ - --use_docker \ - -t \ - -l all \ - -c gcov \ - -x sponge_log.xml \ - -j 16 || FAILED="true" + -l c c++ -x coverage_cpp/sponge_log.xml \ + --use_docker -t -c gcov -j 8 || FAILED="true" + +python tools/run_tests/run_tests.py \ + -l python -x coverage_python/sponge_log.xml \ + --use_docker -t -c gcov -j 8 || FAILED="true" + +python tools/run_tests/run_tests.py \ + -l ruby -x coverage_ruby/sponge_log.xml \ + --use_docker -t -c gcov -j 8 || FAILED="true" + +python tools/run_tests/run_tests.py \ + -l php -x coverage_php/sponge_log.xml \ + --use_docker -t -c gcov -j 8 || FAILED="true" # HTML reports can't be easily displayed in GCS, so create a zip archive # and put it under reports directory to get it uploaded as an artifact. diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index a1f2aaab2f..4bc7ed907c 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -1340,9 +1340,9 @@ argp.add_argument( argp.add_argument( '-l', '--language', - choices=['all'] + sorted(_LANGUAGES.keys()), + choices=sorted(_LANGUAGES.keys()), nargs='+', - default=['all']) + required=True) argp.add_argument( '-S', '--stop_on_failure', default=False, action='store_const', const=True) argp.add_argument( @@ -1513,17 +1513,7 @@ build_config = run_config.build_config if args.travis: _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'} -if 'all' in args.language: - lang_list = list(_LANGUAGES.keys()) -else: - lang_list = args.language -# We don't support code coverage on some languages -if 'gcov' in args.config: - for bad in ['csharp', 'grpc-node', 'objc', 'sanity']: - if bad in lang_list: - lang_list.remove(bad) - -languages = set(_LANGUAGES[l] for l in lang_list) +languages = set(_LANGUAGES[l] for l in args.language) for l in languages: l.configure(run_config, args) @@ -1535,7 +1525,7 @@ if any(language.make_options() for language in languages): ) sys.exit(1) else: - # Combining make options is not clean and just happens to work. It allows C/C++ and C# to build + # Combining make options is not clean and just happens to work. It allows C & C++ to build # together, and is only used under gcov. All other configs should build languages individually. language_make_options = list( set([ -- cgit v1.2.3 From cde2cec2795b5475b52195703810e2bf30efc3c3 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 20 Nov 2018 09:46:30 +0100 Subject: pip install coverage for python_stretch3.7 image --- .../tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile.template | 3 +++ tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile | 3 +++ 2 files changed, 6 insertions(+) (limited to 'templates') diff --git a/templates/tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile.template b/templates/tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile.template index ff342db493..c3602f6c51 100644 --- a/templates/tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile.template +++ b/templates/tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile.template @@ -18,3 +18,6 @@ RUN apt-get update && apt-get -t testing install -y python3.7 python3-all-dev RUN curl https://bootstrap.pypa.io/get-pip.py | python3.7 + + # for Python test coverage reporting + RUN pip install coverage diff --git a/tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile b/tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile index add1cc509d..45291ffdcf 100644 --- a/tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile +++ b/tools/dockerfile/test/python_stretch_3.7_x64/Dockerfile @@ -70,3 +70,6 @@ CMD ["bash"] RUN apt-get update && apt-get -t testing install -y python3.7 python3-all-dev RUN curl https://bootstrap.pypa.io/get-pip.py | python3.7 + +# for Python test coverage reporting +RUN pip install coverage -- cgit v1.2.3 From 96a0db9575988cc669e9081d7a142abeac91a022 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Wed, 21 Nov 2018 13:46:02 -0800 Subject: Use 'preX' when pre-releasing gRPC-C++.podspec --- gRPC-C++.podspec | 2 +- templates/gRPC-C++.podspec.template | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'templates') diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 5aea7c6f8c..8bacc33966 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -24,7 +24,7 @@ Pod::Spec.new do |s| s.name = 'gRPC-C++' # TODO (mxyan): use version that match gRPC version when pod is stabilized # version = '1.17.0-pre2' - version = '0.0.4' + version = '0.0.6-pre2' s.version = version s.summary = 'gRPC C++ library' s.homepage = 'https://grpc.io' diff --git a/templates/gRPC-C++.podspec.template b/templates/gRPC-C++.podspec.template index ab330415af..94d5a4fb09 100644 --- a/templates/gRPC-C++.podspec.template +++ b/templates/gRPC-C++.podspec.template @@ -127,12 +127,20 @@ def ruby_multiline_list(files, indent): return (',\n' + indent*' ').join('\'%s\'' % f for f in files) + + def modify_podspec_version_string(pod_version, grpc_version): + # Append -preX when it is a pre-release + if len(str(grpc_version).split('-')) > 1: + return pod_version + '-' + str(grpc_version).split('-')[-1] + else: + return pod_version + %> Pod::Spec.new do |s| s.name = 'gRPC-C++' # TODO (mxyan): use version that match gRPC version when pod is stabilized # version = '${settings.version}' - version = '0.0.4' + version = '${modify_podspec_version_string('0.0.6', settings.version)}' s.version = version s.summary = 'gRPC C++ library' s.homepage = 'https://grpc.io' -- cgit v1.2.3 From b0bcfb4ea5d5a32828ba5e12eb60efc332562f65 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Thu, 22 Nov 2018 09:53:17 +0100 Subject: use python stretch3.7 image for interop tests --- .../grpc_interop_python/Dockerfile.template | 14 ++------- .../interoptest/grpc_interop_python/Dockerfile | 33 ++++++++++------------ 2 files changed, 18 insertions(+), 29 deletions(-) (limited to 'templates') diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile.template index bf28796de3..f584a2378e 100644 --- a/templates/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile.template +++ b/templates/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile.template @@ -14,15 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - FROM debian:jessie - - <%include file="../../apt_get_basic.include"/> - <%include file="../../python_deps.include"/> - # Install pip and virtualenv for Python 3.4 - RUN curl https://bootstrap.pypa.io/get-pip.py | python3.4 - RUN python3.4 -m pip install virtualenv + <%include file="../../python_stretch.include"/> - <%include file="../../run_tests_addons.include"/> - # Define the default command. - CMD ["bash"] - + RUN apt-get update && apt-get -t testing install -y python3.7 python3-all-dev + RUN curl https://bootstrap.pypa.io/get-pip.py | python3.7 diff --git a/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile b/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile index dadd856740..acad500f90 100644 --- a/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile +++ b/tools/dockerfile/interoptest/grpc_interop_python/Dockerfile @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM debian:jessie - +FROM debian:stretch + # Install Git and basic packages. RUN apt-get update && apt-get install -y \ autoconf \ @@ -49,27 +49,24 @@ RUN apt-get update && apt-get install -y \ # Build profiling RUN apt-get update && apt-get install -y time && apt-get clean -#==================== -# Python dependencies - -# Install dependencies - -RUN apt-get update && apt-get install -y \ - python-all-dev \ - python3-all-dev \ - python-pip +# Google Cloud platform API libraries +RUN apt-get update && apt-get install -y python-pip && apt-get clean +RUN pip install --upgrade google-api-python-client oauth2client -# Install Python packages from PyPI -RUN pip install --upgrade pip==10.0.1 -RUN pip install virtualenv -RUN pip install futures==2.2.0 enum34==1.0.4 protobuf==3.5.2.post1 six==1.10.0 twisted==17.5.0 +# Install Python 2.7 +RUN apt-get update && apt-get install -y python2.7 python-all-dev +RUN curl https://bootstrap.pypa.io/get-pip.py | python2.7 -# Install pip and virtualenv for Python 3.4 -RUN curl https://bootstrap.pypa.io/get-pip.py | python3.4 -RUN python3.4 -m pip install virtualenv +# Add Debian 'testing' repository +RUN echo 'deb http://ftp.de.debian.org/debian testing main' >> /etc/apt/sources.list +RUN echo 'APT::Default-Release "stable";' | tee -a /etc/apt/apt.conf.d/00local RUN mkdir /var/local/jenkins # Define the default command. CMD ["bash"] + + +RUN apt-get update && apt-get -t testing install -y python3.7 python3-all-dev +RUN curl https://bootstrap.pypa.io/get-pip.py | python3.7 -- cgit v1.2.3 From 43599facf4c8b42b8a14d0601556f4231d9d10f8 Mon Sep 17 00:00:00 2001 From: Lidi Zheng Date: Tue, 20 Nov 2018 16:57:08 -0800 Subject: Channelz Python wrapper implementation * Expose the C-Core API in Cython layer * Handle the object translation * Create a separate package for Channelz specifically * Handle nullptr and raise exception if seen one * Translate C++ Channelz unit tests * Adding 5 more invalid query unit tests Adding peripheral utility for grpcio-channelz package * Add to `pylint_code.sh` * Add to Python build script * Add to artifact build script * Add to Bazel * Add to Sphinx module list --- doc/python/sphinx/conf.py | 3 + doc/python/sphinx/grpc_channelz.rst | 12 + doc/python/sphinx/index.rst | 1 + src/proto/grpc/channelz/BUILD | 7 + src/proto/grpc/health/v1/BUILD | 1 - src/python/grpcio/grpc/_cython/BUILD.bazel | 1 + .../grpcio/grpc/_cython/_cygrpc/channelz.pyx.pxi | 56 +++ src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi | 10 + src/python/grpcio/grpc/_cython/cygrpc.pyx | 1 + src/python/grpcio/grpc/_server.py | 4 +- src/python/grpcio_channelz/.gitignore | 6 + src/python/grpcio_channelz/MANIFEST.in | 3 + src/python/grpcio_channelz/README.rst | 9 + src/python/grpcio_channelz/channelz_commands.py | 63 +++ .../grpcio_channelz/grpc_channelz/__init__.py | 13 + .../grpcio_channelz/grpc_channelz/v1/BUILD.bazel | 38 ++ .../grpcio_channelz/grpc_channelz/v1/__init__.py | 13 + .../grpcio_channelz/grpc_channelz/v1/channelz.py | 114 +++++ src/python/grpcio_channelz/grpc_version.py | 17 + src/python/grpcio_channelz/setup.py | 96 +++++ src/python/grpcio_tests/setup.py | 1 + src/python/grpcio_tests/tests/channelz/BUILD.bazel | 15 + src/python/grpcio_tests/tests/channelz/__init__.py | 13 + .../tests/channelz/_channelz_servicer_test.py | 476 +++++++++++++++++++++ src/python/grpcio_tests/tests/tests.json | 1 + .../grpcio_channelz/grpc_version.py.template | 19 + tools/distrib/pylint_code.sh | 1 + tools/run_tests/artifacts/build_artifact_python.sh | 5 + tools/run_tests/helper_scripts/build_python.sh | 5 + 29 files changed, 1002 insertions(+), 2 deletions(-) create mode 100644 doc/python/sphinx/grpc_channelz.rst create mode 100644 src/python/grpcio/grpc/_cython/_cygrpc/channelz.pyx.pxi create mode 100644 src/python/grpcio_channelz/.gitignore create mode 100644 src/python/grpcio_channelz/MANIFEST.in create mode 100644 src/python/grpcio_channelz/README.rst create mode 100644 src/python/grpcio_channelz/channelz_commands.py create mode 100644 src/python/grpcio_channelz/grpc_channelz/__init__.py create mode 100644 src/python/grpcio_channelz/grpc_channelz/v1/BUILD.bazel create mode 100644 src/python/grpcio_channelz/grpc_channelz/v1/__init__.py create mode 100644 src/python/grpcio_channelz/grpc_channelz/v1/channelz.py create mode 100644 src/python/grpcio_channelz/grpc_version.py create mode 100644 src/python/grpcio_channelz/setup.py create mode 100644 src/python/grpcio_tests/tests/channelz/BUILD.bazel create mode 100644 src/python/grpcio_tests/tests/channelz/__init__.py create mode 100644 src/python/grpcio_tests/tests/channelz/_channelz_servicer_test.py create mode 100644 templates/src/python/grpcio_channelz/grpc_version.py.template (limited to 'templates') diff --git a/doc/python/sphinx/conf.py b/doc/python/sphinx/conf.py index 1eb3a5de7f..307c3bdaf6 100644 --- a/doc/python/sphinx/conf.py +++ b/doc/python/sphinx/conf.py @@ -19,6 +19,7 @@ import sys PYTHON_FOLDER = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', '..', 'src', 'python') sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio')) +sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_channelz')) sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_health_checking')) sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_reflection')) sys.path.insert(0, os.path.join(PYTHON_FOLDER, 'grpcio_testing')) @@ -63,6 +64,8 @@ autodoc_default_options = { autodoc_mock_imports = [ 'grpc._cython', + 'grpc_channelz.v1.channelz_pb2', + 'grpc_channelz.v1.channelz_pb2_grpc', 'grpc_health.v1.health_pb2', 'grpc_health.v1.health_pb2_grpc', 'grpc_reflection.v1alpha.reflection_pb2', diff --git a/doc/python/sphinx/grpc_channelz.rst b/doc/python/sphinx/grpc_channelz.rst new file mode 100644 index 0000000000..f65793a071 --- /dev/null +++ b/doc/python/sphinx/grpc_channelz.rst @@ -0,0 +1,12 @@ +gRPC Channelz +==================== + +What is gRPC Channelz? +--------------------------------------------- + +Design Document `gRPC Channelz `_ + +Module Contents +--------------- + +.. automodule:: grpc_channelz.v1.channelz diff --git a/doc/python/sphinx/index.rst b/doc/python/sphinx/index.rst index 322ca33e15..2f8a47a074 100644 --- a/doc/python/sphinx/index.rst +++ b/doc/python/sphinx/index.rst @@ -10,6 +10,7 @@ API Reference :caption: Contents: grpc + grpc_channelz grpc_health_checking grpc_reflection grpc_testing diff --git a/src/proto/grpc/channelz/BUILD b/src/proto/grpc/channelz/BUILD index bdb03d5e2d..b6b485e3e8 100644 --- a/src/proto/grpc/channelz/BUILD +++ b/src/proto/grpc/channelz/BUILD @@ -24,3 +24,10 @@ grpc_proto_library( has_services = True, well_known_protos = True, ) + +filegroup( + name = "channelz_proto_file", + srcs = [ + "channelz.proto", + ], +) diff --git a/src/proto/grpc/health/v1/BUILD b/src/proto/grpc/health/v1/BUILD index 97642985c9..38a7d992e0 100644 --- a/src/proto/grpc/health/v1/BUILD +++ b/src/proto/grpc/health/v1/BUILD @@ -29,4 +29,3 @@ filegroup( "health.proto", ], ) - diff --git a/src/python/grpcio/grpc/_cython/BUILD.bazel b/src/python/grpcio/grpc/_cython/BUILD.bazel index cfd3a51d9b..e318298d0a 100644 --- a/src/python/grpcio/grpc/_cython/BUILD.bazel +++ b/src/python/grpcio/grpc/_cython/BUILD.bazel @@ -12,6 +12,7 @@ pyx_library( "_cygrpc/grpc_string.pyx.pxi", "_cygrpc/arguments.pyx.pxi", "_cygrpc/call.pyx.pxi", + "_cygrpc/channelz.pyx.pxi", "_cygrpc/channel.pyx.pxi", "_cygrpc/credentials.pyx.pxi", "_cygrpc/completion_queue.pyx.pxi", diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channelz.pyx.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/channelz.pyx.pxi new file mode 100644 index 0000000000..572a473d3e --- /dev/null +++ b/src/python/grpcio/grpc/_cython/_cygrpc/channelz.pyx.pxi @@ -0,0 +1,56 @@ +# Copyright 2018 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def channelz_get_top_channels(start_channel_id): + cdef char *c_returned_str = grpc_channelz_get_top_channels(start_channel_id) + if c_returned_str == NULL: + raise ValueError('Failed to get top channels, please ensure your start_channel_id==%s is valid' % start_channel_id) + return c_returned_str + +def channelz_get_servers(start_server_id): + cdef char *c_returned_str = grpc_channelz_get_servers(start_server_id) + if c_returned_str == NULL: + raise ValueError('Failed to get servers, please ensure your start_server_id==%s is valid' % start_server_id) + return c_returned_str + +def channelz_get_server(server_id): + cdef char *c_returned_str = grpc_channelz_get_server(server_id) + if c_returned_str == NULL: + raise ValueError('Failed to get the server, please ensure your server_id==%s is valid' % server_id) + return c_returned_str + +def channelz_get_server_sockets(server_id, start_socket_id): + cdef char *c_returned_str = grpc_channelz_get_server_sockets(server_id, start_socket_id) + if c_returned_str == NULL: + raise ValueError('Failed to get server sockets, please ensure your server_id==%s and start_socket_id==%s is valid' % (server_id, start_socket_id)) + return c_returned_str + +def channelz_get_channel(channel_id): + cdef char *c_returned_str = grpc_channelz_get_channel(channel_id) + if c_returned_str == NULL: + raise ValueError('Failed to get the channel, please ensure your channel_id==%s is valid' % (channel_id)) + return c_returned_str + +def channelz_get_subchannel(subchannel_id): + cdef char *c_returned_str = grpc_channelz_get_subchannel(subchannel_id) + if c_returned_str == NULL: + raise ValueError('Failed to get the subchannel, please ensure your subchannel_id==%s is valid' % (subchannel_id)) + return c_returned_str + +def channelz_get_socket(socket_id): + cdef char *c_returned_str = grpc_channelz_get_socket(socket_id) + if c_returned_str == NULL: + raise ValueError('Failed to get the socket, please ensure your socket_id==%s is valid' % (socket_id)) + return c_returned_str diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi index 23428f0b0c..ccd46dbf92 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxi @@ -13,6 +13,7 @@ # limitations under the License. cimport libc.time +from libc.stdint cimport intptr_t # Typedef types with approximately the same semantics to provide their names to @@ -391,6 +392,15 @@ cdef extern from "grpc/grpc.h": void grpc_server_cancel_all_calls(grpc_server *server) nogil void grpc_server_destroy(grpc_server *server) nogil + char* grpc_channelz_get_top_channels(intptr_t start_channel_id) + char* grpc_channelz_get_servers(intptr_t start_server_id) + char* grpc_channelz_get_server(intptr_t server_id) + char* grpc_channelz_get_server_sockets(intptr_t server_id, + intptr_t start_socket_id) + char* grpc_channelz_get_channel(intptr_t channel_id) + char* grpc_channelz_get_subchannel(intptr_t subchannel_id) + char* grpc_channelz_get_socket(intptr_t socket_id) + cdef extern from "grpc/grpc_security.h": diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx index ae5c07bfc8..9ab919375c 100644 --- a/src/python/grpcio/grpc/_cython/cygrpc.pyx +++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx @@ -35,6 +35,7 @@ include "_cygrpc/server.pyx.pxi" include "_cygrpc/tag.pyx.pxi" include "_cygrpc/time.pyx.pxi" include "_cygrpc/_hooks.pyx.pxi" +include "_cygrpc/channelz.pyx.pxi" include "_cygrpc/grpc_gevent.pyx.pxi" diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py index 7276a7fd90..fea1ca8da6 100644 --- a/src/python/grpcio/grpc/_server.py +++ b/src/python/grpcio/grpc/_server.py @@ -828,7 +828,9 @@ class _Server(grpc.Server): return _stop(self._state, grace) def __del__(self): - _stop(self._state, None) + if hasattr(self, '_state'): + _stop(self._state, None) + del self._state def create_server(thread_pool, generic_rpc_handlers, interceptors, options, diff --git a/src/python/grpcio_channelz/.gitignore b/src/python/grpcio_channelz/.gitignore new file mode 100644 index 0000000000..0c5da6b5af --- /dev/null +++ b/src/python/grpcio_channelz/.gitignore @@ -0,0 +1,6 @@ +*.proto +*_pb2.py +*_pb2_grpc.py +build/ +grpcio_channelz.egg-info/ +dist/ diff --git a/src/python/grpcio_channelz/MANIFEST.in b/src/python/grpcio_channelz/MANIFEST.in new file mode 100644 index 0000000000..5597f375ba --- /dev/null +++ b/src/python/grpcio_channelz/MANIFEST.in @@ -0,0 +1,3 @@ +include grpc_version.py +recursive-include grpc_channelz *.py +global-exclude *.pyc diff --git a/src/python/grpcio_channelz/README.rst b/src/python/grpcio_channelz/README.rst new file mode 100644 index 0000000000..efeaa56064 --- /dev/null +++ b/src/python/grpcio_channelz/README.rst @@ -0,0 +1,9 @@ +gRPC Python Channelz package +============================== + +Channelz is a live debug tool in gRPC Python. + +Dependencies +------------ + +Depends on the `grpcio` package, available from PyPI via `pip install grpcio`. diff --git a/src/python/grpcio_channelz/channelz_commands.py b/src/python/grpcio_channelz/channelz_commands.py new file mode 100644 index 0000000000..e9ad355034 --- /dev/null +++ b/src/python/grpcio_channelz/channelz_commands.py @@ -0,0 +1,63 @@ +# Copyright 2018 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Provides distutils command classes for the GRPC Python setup process.""" + +import os +import shutil + +import setuptools + +ROOT_DIR = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) +CHANNELZ_PROTO = os.path.join(ROOT_DIR, + '../../proto/grpc/channelz/channelz.proto') + + +class CopyProtoModules(setuptools.Command): + """Command to copy proto modules from grpc/src/proto.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + if os.path.isfile(CHANNELZ_PROTO): + shutil.copyfile(CHANNELZ_PROTO, + os.path.join(ROOT_DIR, + 'grpc_channelz/v1/channelz.proto')) + + +class BuildPackageProtos(setuptools.Command): + """Command to generate project *_pb2.py modules from proto files.""" + + description = 'build grpc protobuf modules' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + # due to limitations of the proto generator, we require that only *one* + # directory is provided as an 'include' directory. We assume it's the '' key + # to `self.distribution.package_dir` (and get a key error if it's not + # there). + from grpc_tools import command + command.build_package_protos(self.distribution.package_dir['']) diff --git a/src/python/grpcio_channelz/grpc_channelz/__init__.py b/src/python/grpcio_channelz/grpc_channelz/__init__.py new file mode 100644 index 0000000000..38fdfc9c5c --- /dev/null +++ b/src/python/grpcio_channelz/grpc_channelz/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/python/grpcio_channelz/grpc_channelz/v1/BUILD.bazel b/src/python/grpcio_channelz/grpc_channelz/v1/BUILD.bazel new file mode 100644 index 0000000000..aae8cedb76 --- /dev/null +++ b/src/python/grpcio_channelz/grpc_channelz/v1/BUILD.bazel @@ -0,0 +1,38 @@ +load("@grpc_python_dependencies//:requirements.bzl", "requirement") +load("@org_pubref_rules_protobuf//python:rules.bzl", "py_proto_library") + +package(default_visibility = ["//visibility:public"]) + +genrule( + name = "mv_channelz_proto", + srcs = [ + "//src/proto/grpc/channelz:channelz_proto_file", + ], + outs = ["channelz.proto",], + cmd = "cp $< $@", +) + +py_proto_library( + name = "py_channelz_proto", + protos = ["mv_channelz_proto",], + imports = [ + "external/com_google_protobuf/src/", + ], + inputs = [ + "@com_google_protobuf//:well_known_protos", + ], + with_grpc = True, + deps = [ + requirement('protobuf'), + ], +) + +py_library( + name = "grpc_channelz", + srcs = ["channelz.py",], + deps = [ + ":py_channelz_proto", + "//src/python/grpcio/grpc:grpcio", + ], + imports=["../../",], +) diff --git a/src/python/grpcio_channelz/grpc_channelz/v1/__init__.py b/src/python/grpcio_channelz/grpc_channelz/v1/__init__.py new file mode 100644 index 0000000000..38fdfc9c5c --- /dev/null +++ b/src/python/grpcio_channelz/grpc_channelz/v1/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/python/grpcio_channelz/grpc_channelz/v1/channelz.py b/src/python/grpcio_channelz/grpc_channelz/v1/channelz.py new file mode 100644 index 0000000000..bc08562b7b --- /dev/null +++ b/src/python/grpcio_channelz/grpc_channelz/v1/channelz.py @@ -0,0 +1,114 @@ +# Copyright 2018 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Channelz debug service implementation in gRPC Python.""" + +import grpc +from grpc._cython import cygrpc + +import grpc_channelz.v1.channelz_pb2 as _channelz_pb2 +import grpc_channelz.v1.channelz_pb2_grpc as _channelz_pb2_grpc + +from google.protobuf import json_format + + +class ChannelzServicer(_channelz_pb2_grpc.ChannelzServicer): + """Servicer handling RPCs for service statuses.""" + + # pylint: disable=no-self-use + def GetTopChannels(self, request, context): + try: + return json_format.Parse( + cygrpc.channelz_get_top_channels(request.start_channel_id), + _channelz_pb2.GetTopChannelsResponse(), + ) + except ValueError as e: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details(str(e)) + + # pylint: disable=no-self-use + def GetServers(self, request, context): + try: + return json_format.Parse( + cygrpc.channelz_get_servers(request.start_server_id), + _channelz_pb2.GetServersResponse(), + ) + except ValueError as e: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details(str(e)) + + # pylint: disable=no-self-use + def GetServer(self, request, context): + try: + return json_format.Parse( + cygrpc.channelz_get_server(request.server_id), + _channelz_pb2.GetServerResponse(), + ) + except ValueError as e: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details(str(e)) + + # pylint: disable=no-self-use + def GetServerSockets(self, request, context): + try: + return json_format.Parse( + cygrpc.channelz_get_server_sockets(request.server_id, + request.start_socket_id), + _channelz_pb2.GetServerSocketsResponse(), + ) + except ValueError as e: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details(str(e)) + + # pylint: disable=no-self-use + def GetChannel(self, request, context): + try: + return json_format.Parse( + cygrpc.channelz_get_channel(request.channel_id), + _channelz_pb2.GetChannelResponse(), + ) + except ValueError as e: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details(str(e)) + + # pylint: disable=no-self-use + def GetSubchannel(self, request, context): + try: + return json_format.Parse( + cygrpc.channelz_get_subchannel(request.subchannel_id), + _channelz_pb2.GetSubchannelResponse(), + ) + except ValueError as e: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details(str(e)) + + # pylint: disable=no-self-use + def GetSocket(self, request, context): + try: + return json_format.Parse( + cygrpc.channelz_get_socket(request.socket_id), + _channelz_pb2.GetSocketResponse(), + ) + except ValueError as e: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) + context.set_details(str(e)) + + +def enable_channelz(server): + """Enables Channelz on a server. + + Args: + server: grpc.Server to which Channelz service will be added. + """ + _channelz_pb2_grpc.add_ChannelzServicer_to_server(ChannelzServicer(), + server) diff --git a/src/python/grpcio_channelz/grpc_version.py b/src/python/grpcio_channelz/grpc_version.py new file mode 100644 index 0000000000..16356ea402 --- /dev/null +++ b/src/python/grpcio_channelz/grpc_version.py @@ -0,0 +1,17 @@ +# Copyright 2018 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_channelz/grpc_version.py.template`!!! + +VERSION = '1.18.0.dev0' diff --git a/src/python/grpcio_channelz/setup.py b/src/python/grpcio_channelz/setup.py new file mode 100644 index 0000000000..a495052376 --- /dev/null +++ b/src/python/grpcio_channelz/setup.py @@ -0,0 +1,96 @@ +# Copyright 2018 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Setup module for the GRPC Python package's Channelz.""" + +import os +import sys + +import setuptools + +# Ensure we're in the proper directory whether or not we're being used by pip. +os.chdir(os.path.dirname(os.path.abspath(__file__))) + +# Break import-style to ensure we can actually find our local modules. +import grpc_version + + +class _NoOpCommand(setuptools.Command): + """No-op command.""" + + description = '' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + pass + + +CLASSIFIERS = [ + 'Development Status :: 5 - Production/Stable', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'License :: OSI Approved :: Apache Software License', +] + +PACKAGE_DIRECTORIES = { + '': '.', +} + +INSTALL_REQUIRES = ( + 'protobuf>=3.6.0', + 'grpcio>={version}'.format(version=grpc_version.VERSION), +) + +try: + import channelz_commands as _channelz_commands + # we are in the build environment, otherwise the above import fails + SETUP_REQUIRES = ( + 'grpcio-tools=={version}'.format(version=grpc_version.VERSION),) + COMMAND_CLASS = { + # Run preprocess from the repository *before* doing any packaging! + 'preprocess': _channelz_commands.CopyProtoModules, + 'build_package_protos': _channelz_commands.BuildPackageProtos, + } +except ImportError: + SETUP_REQUIRES = () + COMMAND_CLASS = { + # wire up commands to no-op not to break the external dependencies + 'preprocess': _NoOpCommand, + 'build_package_protos': _NoOpCommand, + } + +setuptools.setup( + name='grpcio-channelz', + version=grpc_version.VERSION, + license='Apache License 2.0', + description='Channel Level Live Debug Information Service for gRPC', + author='The gRPC Authors', + author_email='grpc-io@googlegroups.com', + classifiers=CLASSIFIERS, + url='https://grpc.io', + package_dir=PACKAGE_DIRECTORIES, + packages=setuptools.find_packages('.'), + install_requires=INSTALL_REQUIRES, + setup_requires=SETUP_REQUIRES, + cmdclass=COMMAND_CLASS) diff --git a/src/python/grpcio_tests/setup.py b/src/python/grpcio_tests/setup.py index 61c98fa038..f56425ac6d 100644 --- a/src/python/grpcio_tests/setup.py +++ b/src/python/grpcio_tests/setup.py @@ -39,6 +39,7 @@ PACKAGE_DIRECTORIES = { INSTALL_REQUIRES = ( 'coverage>=4.0', 'enum34>=1.0.4', 'grpcio>={version}'.format(version=grpc_version.VERSION), + 'grpcio-channelz>={version}'.format(version=grpc_version.VERSION), 'grpcio-tools>={version}'.format(version=grpc_version.VERSION), 'grpcio-health-checking>={version}'.format(version=grpc_version.VERSION), 'oauth2client>=1.4.7', 'protobuf>=3.6.0', 'six>=1.10', 'google-auth>=1.0.0', diff --git a/src/python/grpcio_tests/tests/channelz/BUILD.bazel b/src/python/grpcio_tests/tests/channelz/BUILD.bazel new file mode 100644 index 0000000000..63513616e7 --- /dev/null +++ b/src/python/grpcio_tests/tests/channelz/BUILD.bazel @@ -0,0 +1,15 @@ +package(default_visibility = ["//visibility:public"]) + +py_test( + name = "channelz_servicer_test", + srcs = ["_channelz_servicer_test.py"], + main = "_channelz_servicer_test.py", + size = "small", + deps = [ + "//src/python/grpcio/grpc:grpcio", + "//src/python/grpcio_channelz/grpc_channelz/v1:grpc_channelz", + "//src/python/grpcio_tests/tests/unit:test_common", + "//src/python/grpcio_tests/tests/unit/framework/common:common", + ], + imports = ["../../",], +) diff --git a/src/python/grpcio_tests/tests/channelz/__init__.py b/src/python/grpcio_tests/tests/channelz/__init__.py new file mode 100644 index 0000000000..38fdfc9c5c --- /dev/null +++ b/src/python/grpcio_tests/tests/channelz/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2018 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/python/grpcio_tests/tests/channelz/_channelz_servicer_test.py b/src/python/grpcio_tests/tests/channelz/_channelz_servicer_test.py new file mode 100644 index 0000000000..103609441e --- /dev/null +++ b/src/python/grpcio_tests/tests/channelz/_channelz_servicer_test.py @@ -0,0 +1,476 @@ +# Copyright 2018 The gRPC Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests of grpc_channelz.v1.channelz.""" + +import unittest + +from concurrent import futures + +import grpc +from grpc_channelz.v1 import channelz +from grpc_channelz.v1 import channelz_pb2 +from grpc_channelz.v1 import channelz_pb2_grpc + +from tests.unit import test_common +from tests.unit.framework.common import test_constants + +_SUCCESSFUL_UNARY_UNARY = '/test/SuccessfulUnaryUnary' +_FAILED_UNARY_UNARY = '/test/FailedUnaryUnary' +_SUCCESSFUL_STREAM_STREAM = '/test/SuccessfulStreamStream' + +_REQUEST = b'\x00\x00\x00' +_RESPONSE = b'\x01\x01\x01' + +_DISABLE_REUSE_PORT = (('grpc.so_reuseport', 0),) +_ENABLE_CHANNELZ = (('grpc.enable_channelz', 1),) +_DISABLE_CHANNELZ = (('grpc.enable_channelz', 0),) + + +def _successful_unary_unary(request, servicer_context): + return _RESPONSE + + +def _failed_unary_unary(request, servicer_context): + servicer_context.set_code(grpc.StatusCode.INTERNAL) + servicer_context.set_details("Channelz Test Intended Failure") + + +def _successful_stream_stream(request_iterator, servicer_context): + for _ in request_iterator: + yield _RESPONSE + + +class _GenericHandler(grpc.GenericRpcHandler): + + def service(self, handler_call_details): + if handler_call_details.method == _SUCCESSFUL_UNARY_UNARY: + return grpc.unary_unary_rpc_method_handler(_successful_unary_unary) + elif handler_call_details.method == _FAILED_UNARY_UNARY: + return grpc.unary_unary_rpc_method_handler(_failed_unary_unary) + elif handler_call_details.method == _SUCCESSFUL_STREAM_STREAM: + return grpc.stream_stream_rpc_method_handler( + _successful_stream_stream) + else: + return None + + +class _ChannelServerPair(object): + + def __init__(self): + # Server will enable channelz service + # Bind as attribute to make it gc properly + self._server = grpc.server( + futures.ThreadPoolExecutor(max_workers=3), + options=_DISABLE_REUSE_PORT + _ENABLE_CHANNELZ) + port = self._server.add_insecure_port('[::]:0') + self._server.add_generic_rpc_handlers((_GenericHandler(),)) + self._server.start() + + # Channel will enable channelz service... + self.channel = grpc.insecure_channel('localhost:%d' % port, + _ENABLE_CHANNELZ) + + def __del__(self): + self._server.__del__() + self.channel.close() + + +def _generate_channel_server_pairs(n): + return [_ChannelServerPair() for i in range(n)] + + +def _clean_channel_server_pairs(pairs): + for pair in pairs: + pair.__del__() + + +class ChannelzServicerTest(unittest.TestCase): + + def _send_successful_unary_unary(self, idx): + _, r = self._pairs[idx].channel.unary_unary( + _SUCCESSFUL_UNARY_UNARY).with_call(_REQUEST) + self.assertEqual(r.code(), grpc.StatusCode.OK) + + def _send_failed_unary_unary(self, idx): + try: + self._pairs[idx].channel.unary_unary(_FAILED_UNARY_UNARY).with_call( + _REQUEST) + except grpc.RpcError: + return + else: + self.fail("This call supposed to fail") + + def _send_successful_stream_stream(self, idx): + response_iterator = self._pairs[idx].channel.stream_stream( + _SUCCESSFUL_STREAM_STREAM).__call__( + iter([_REQUEST] * test_constants.STREAM_LENGTH)) + cnt = 0 + for _ in response_iterator: + cnt += 1 + self.assertEqual(cnt, test_constants.STREAM_LENGTH) + + def _get_channel_id(self, idx): + """Channel id may not be consecutive""" + resp = self._channelz_stub.GetTopChannels( + channelz_pb2.GetTopChannelsRequest(start_channel_id=0)) + self.assertGreater(len(resp.channel), idx) + return resp.channel[idx].ref.channel_id + + def setUp(self): + # This server is for Channelz info fetching only + # It self should not enable Channelz + self._server = grpc.server( + futures.ThreadPoolExecutor(max_workers=3), + options=_DISABLE_REUSE_PORT + _DISABLE_CHANNELZ) + port = self._server.add_insecure_port('[::]:0') + channelz_pb2_grpc.add_ChannelzServicer_to_server( + channelz.ChannelzServicer(), + self._server, + ) + self._server.start() + + # This channel is used to fetch Channelz info only + # Channelz should not be enabled + self._channel = grpc.insecure_channel('localhost:%d' % port, + _DISABLE_CHANNELZ) + self._channelz_stub = channelz_pb2_grpc.ChannelzStub(self._channel) + + def tearDown(self): + self._server.__del__() + self._channel.close() + # _pairs may not exist, if the test crashed during setup + if hasattr(self, '_pairs'): + _clean_channel_server_pairs(self._pairs) + + def test_get_top_channels_basic(self): + self._pairs = _generate_channel_server_pairs(1) + resp = self._channelz_stub.GetTopChannels( + channelz_pb2.GetTopChannelsRequest(start_channel_id=0)) + self.assertEqual(len(resp.channel), 1) + self.assertEqual(resp.end, True) + + def test_get_top_channels_high_start_id(self): + self._pairs = _generate_channel_server_pairs(1) + resp = self._channelz_stub.GetTopChannels( + channelz_pb2.GetTopChannelsRequest(start_channel_id=10000)) + self.assertEqual(len(resp.channel), 0) + self.assertEqual(resp.end, True) + + def test_successful_request(self): + self._pairs = _generate_channel_server_pairs(1) + self._send_successful_unary_unary(0) + resp = self._channelz_stub.GetChannel( + channelz_pb2.GetChannelRequest(channel_id=self._get_channel_id(0))) + self.assertEqual(resp.channel.data.calls_started, 1) + self.assertEqual(resp.channel.data.calls_succeeded, 1) + self.assertEqual(resp.channel.data.calls_failed, 0) + + def test_failed_request(self): + self._pairs = _generate_channel_server_pairs(1) + self._send_failed_unary_unary(0) + resp = self._channelz_stub.GetChannel( + channelz_pb2.GetChannelRequest(channel_id=self._get_channel_id(0))) + self.assertEqual(resp.channel.data.calls_started, 1) + self.assertEqual(resp.channel.data.calls_succeeded, 0) + self.assertEqual(resp.channel.data.calls_failed, 1) + + def test_many_requests(self): + self._pairs = _generate_channel_server_pairs(1) + k_success = 7 + k_failed = 9 + for i in range(k_success): + self._send_successful_unary_unary(0) + for i in range(k_failed): + self._send_failed_unary_unary(0) + resp = self._channelz_stub.GetChannel( + channelz_pb2.GetChannelRequest(channel_id=self._get_channel_id(0))) + self.assertEqual(resp.channel.data.calls_started, k_success + k_failed) + self.assertEqual(resp.channel.data.calls_succeeded, k_success) + self.assertEqual(resp.channel.data.calls_failed, k_failed) + + def test_many_channel(self): + k_channels = 4 + self._pairs = _generate_channel_server_pairs(k_channels) + resp = self._channelz_stub.GetTopChannels( + channelz_pb2.GetTopChannelsRequest(start_channel_id=0)) + self.assertEqual(len(resp.channel), k_channels) + + def test_many_requests_many_channel(self): + k_channels = 4 + self._pairs = _generate_channel_server_pairs(k_channels) + k_success = 11 + k_failed = 13 + for i in range(k_success): + self._send_successful_unary_unary(0) + self._send_successful_unary_unary(2) + for i in range(k_failed): + self._send_failed_unary_unary(1) + self._send_failed_unary_unary(2) + + # The first channel saw only successes + resp = self._channelz_stub.GetChannel( + channelz_pb2.GetChannelRequest(channel_id=self._get_channel_id(0))) + self.assertEqual(resp.channel.data.calls_started, k_success) + self.assertEqual(resp.channel.data.calls_succeeded, k_success) + self.assertEqual(resp.channel.data.calls_failed, 0) + + # The second channel saw only failures + resp = self._channelz_stub.GetChannel( + channelz_pb2.GetChannelRequest(channel_id=self._get_channel_id(1))) + self.assertEqual(resp.channel.data.calls_started, k_failed) + self.assertEqual(resp.channel.data.calls_succeeded, 0) + self.assertEqual(resp.channel.data.calls_failed, k_failed) + + # The third channel saw both successes and failures + resp = self._channelz_stub.GetChannel( + channelz_pb2.GetChannelRequest(channel_id=self._get_channel_id(2))) + self.assertEqual(resp.channel.data.calls_started, k_success + k_failed) + self.assertEqual(resp.channel.data.calls_succeeded, k_success) + self.assertEqual(resp.channel.data.calls_failed, k_failed) + + # The fourth channel saw nothing + resp = self._channelz_stub.GetChannel( + channelz_pb2.GetChannelRequest(channel_id=self._get_channel_id(3))) + self.assertEqual(resp.channel.data.calls_started, 0) + self.assertEqual(resp.channel.data.calls_succeeded, 0) + self.assertEqual(resp.channel.data.calls_failed, 0) + + def test_many_subchannels(self): + k_channels = 4 + self._pairs = _generate_channel_server_pairs(k_channels) + k_success = 17 + k_failed = 19 + for i in range(k_success): + self._send_successful_unary_unary(0) + self._send_successful_unary_unary(2) + for i in range(k_failed): + self._send_failed_unary_unary(1) + self._send_failed_unary_unary(2) + + gtc_resp = self._channelz_stub.GetTopChannels( + channelz_pb2.GetTopChannelsRequest(start_channel_id=0)) + self.assertEqual(len(gtc_resp.channel), k_channels) + for i in range(k_channels): + # If no call performed in the channel, there shouldn't be any subchannel + if gtc_resp.channel[i].data.calls_started == 0: + self.assertEqual(len(gtc_resp.channel[i].subchannel_ref), 0) + continue + + # Otherwise, the subchannel should exist + self.assertGreater(len(gtc_resp.channel[i].subchannel_ref), 0) + gsc_resp = self._channelz_stub.GetSubchannel( + channelz_pb2.GetSubchannelRequest( + subchannel_id=gtc_resp.channel[i].subchannel_ref[ + 0].subchannel_id)) + self.assertEqual(gtc_resp.channel[i].data.calls_started, + gsc_resp.subchannel.data.calls_started) + self.assertEqual(gtc_resp.channel[i].data.calls_succeeded, + gsc_resp.subchannel.data.calls_succeeded) + self.assertEqual(gtc_resp.channel[i].data.calls_failed, + gsc_resp.subchannel.data.calls_failed) + + def test_server_basic(self): + self._pairs = _generate_channel_server_pairs(1) + resp = self._channelz_stub.GetServers( + channelz_pb2.GetServersRequest(start_server_id=0)) + self.assertEqual(len(resp.server), 1) + + def test_get_one_server(self): + self._pairs = _generate_channel_server_pairs(1) + gss_resp = self._channelz_stub.GetServers( + channelz_pb2.GetServersRequest(start_server_id=0)) + self.assertEqual(len(gss_resp.server), 1) + gs_resp = self._channelz_stub.GetServer( + channelz_pb2.GetServerRequest( + server_id=gss_resp.server[0].ref.server_id)) + self.assertEqual(gss_resp.server[0].ref.server_id, + gs_resp.server.ref.server_id) + + def test_server_call(self): + self._pairs = _generate_channel_server_pairs(1) + k_success = 23 + k_failed = 29 + for i in range(k_success): + self._send_successful_unary_unary(0) + for i in range(k_failed): + self._send_failed_unary_unary(0) + + resp = self._channelz_stub.GetServers( + channelz_pb2.GetServersRequest(start_server_id=0)) + self.assertEqual(len(resp.server), 1) + self.assertEqual(resp.server[0].data.calls_started, + k_success + k_failed) + self.assertEqual(resp.server[0].data.calls_succeeded, k_success) + self.assertEqual(resp.server[0].data.calls_failed, k_failed) + + def test_many_subchannels_and_sockets(self): + k_channels = 4 + self._pairs = _generate_channel_server_pairs(k_channels) + k_success = 3 + k_failed = 5 + for i in range(k_success): + self._send_successful_unary_unary(0) + self._send_successful_unary_unary(2) + for i in range(k_failed): + self._send_failed_unary_unary(1) + self._send_failed_unary_unary(2) + + gtc_resp = self._channelz_stub.GetTopChannels( + channelz_pb2.GetTopChannelsRequest(start_channel_id=0)) + self.assertEqual(len(gtc_resp.channel), k_channels) + for i in range(k_channels): + # If no call performed in the channel, there shouldn't be any subchannel + if gtc_resp.channel[i].data.calls_started == 0: + self.assertEqual(len(gtc_resp.channel[i].subchannel_ref), 0) + continue + + # Otherwise, the subchannel should exist + self.assertGreater(len(gtc_resp.channel[i].subchannel_ref), 0) + gsc_resp = self._channelz_stub.GetSubchannel( + channelz_pb2.GetSubchannelRequest( + subchannel_id=gtc_resp.channel[i].subchannel_ref[ + 0].subchannel_id)) + self.assertEqual(len(gsc_resp.subchannel.socket_ref), 1) + + gs_resp = self._channelz_stub.GetSocket( + channelz_pb2.GetSocketRequest( + socket_id=gsc_resp.subchannel.socket_ref[0].socket_id)) + self.assertEqual(gsc_resp.subchannel.data.calls_started, + gs_resp.socket.data.streams_started) + self.assertEqual(gsc_resp.subchannel.data.calls_started, + gs_resp.socket.data.streams_succeeded) + # Calls started == messages sent, only valid for unary calls + self.assertEqual(gsc_resp.subchannel.data.calls_started, + gs_resp.socket.data.messages_sent) + # Only receive responses when the RPC was successful + self.assertEqual(gsc_resp.subchannel.data.calls_succeeded, + gs_resp.socket.data.messages_received) + + def test_streaming_rpc(self): + self._pairs = _generate_channel_server_pairs(1) + # In C++, the argument for _send_successful_stream_stream is message length. + # Here the argument is still channel idx, to be consistent with the other two. + self._send_successful_stream_stream(0) + + gc_resp = self._channelz_stub.GetChannel( + channelz_pb2.GetChannelRequest(channel_id=self._get_channel_id(0))) + self.assertEqual(gc_resp.channel.data.calls_started, 1) + self.assertEqual(gc_resp.channel.data.calls_succeeded, 1) + self.assertEqual(gc_resp.channel.data.calls_failed, 0) + # Subchannel exists + self.assertGreater(len(gc_resp.channel.subchannel_ref), 0) + + gsc_resp = self._channelz_stub.GetSubchannel( + channelz_pb2.GetSubchannelRequest( + subchannel_id=gc_resp.channel.subchannel_ref[0].subchannel_id)) + self.assertEqual(gsc_resp.subchannel.data.calls_started, 1) + self.assertEqual(gsc_resp.subchannel.data.calls_succeeded, 1) + self.assertEqual(gsc_resp.subchannel.data.calls_failed, 0) + # Socket exists + self.assertEqual(len(gsc_resp.subchannel.socket_ref), 1) + + gs_resp = self._channelz_stub.GetSocket( + channelz_pb2.GetSocketRequest( + socket_id=gsc_resp.subchannel.socket_ref[0].socket_id)) + self.assertEqual(gs_resp.socket.data.streams_started, 1) + self.assertEqual(gs_resp.socket.data.streams_succeeded, 1) + self.assertEqual(gs_resp.socket.data.streams_failed, 0) + self.assertEqual(gs_resp.socket.data.messages_sent, + test_constants.STREAM_LENGTH) + self.assertEqual(gs_resp.socket.data.messages_received, + test_constants.STREAM_LENGTH) + + def test_server_sockets(self): + self._pairs = _generate_channel_server_pairs(1) + self._send_successful_unary_unary(0) + self._send_failed_unary_unary(0) + + gs_resp = self._channelz_stub.GetServers( + channelz_pb2.GetServersRequest(start_server_id=0)) + self.assertEqual(len(gs_resp.server), 1) + self.assertEqual(gs_resp.server[0].data.calls_started, 2) + self.assertEqual(gs_resp.server[0].data.calls_succeeded, 1) + self.assertEqual(gs_resp.server[0].data.calls_failed, 1) + + gss_resp = self._channelz_stub.GetServerSockets( + channelz_pb2.GetServerSocketsRequest( + server_id=gs_resp.server[0].ref.server_id, start_socket_id=0)) + # If the RPC call failed, it will raise a grpc.RpcError + # So, if there is no exception raised, considered pass + + def test_server_listen_sockets(self): + self._pairs = _generate_channel_server_pairs(1) + + gss_resp = self._channelz_stub.GetServers( + channelz_pb2.GetServersRequest(start_server_id=0)) + self.assertEqual(len(gss_resp.server), 1) + self.assertEqual(len(gss_resp.server[0].listen_socket), 1) + + gs_resp = self._channelz_stub.GetSocket( + channelz_pb2.GetSocketRequest( + socket_id=gss_resp.server[0].listen_socket[0].socket_id)) + # If the RPC call failed, it will raise a grpc.RpcError + # So, if there is no exception raised, considered pass + + def test_invalid_query_get_server(self): + try: + self._channelz_stub.GetServer( + channelz_pb2.GetServerRequest(server_id=10000)) + except BaseException as e: + self.assertIn('StatusCode.INVALID_ARGUMENT', str(e)) + else: + self.fail('Invalid query not detected') + + def test_invalid_query_get_channel(self): + try: + self._channelz_stub.GetChannel( + channelz_pb2.GetChannelRequest(channel_id=10000)) + except BaseException as e: + self.assertIn('StatusCode.INVALID_ARGUMENT', str(e)) + else: + self.fail('Invalid query not detected') + + def test_invalid_query_get_subchannel(self): + try: + self._channelz_stub.GetSubchannel( + channelz_pb2.GetSubchannelRequest(subchannel_id=10000)) + except BaseException as e: + self.assertIn('StatusCode.INVALID_ARGUMENT', str(e)) + else: + self.fail('Invalid query not detected') + + def test_invalid_query_get_socket(self): + try: + self._channelz_stub.GetSocket( + channelz_pb2.GetSocketRequest(socket_id=10000)) + except BaseException as e: + self.assertIn('StatusCode.INVALID_ARGUMENT', str(e)) + else: + self.fail('Invalid query not detected') + + def test_invalid_query_get_server_sockets(self): + try: + self._channelz_stub.GetServerSockets( + channelz_pb2.GetServerSocketsRequest( + server_id=10000, + start_socket_id=0, + )) + except BaseException as e: + self.assertIn('StatusCode.INVALID_ARGUMENT', str(e)) + else: + self.fail('Invalid query not detected') + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json index a3006d9afc..9cffd3df19 100644 --- a/src/python/grpcio_tests/tests/tests.json +++ b/src/python/grpcio_tests/tests/tests.json @@ -1,5 +1,6 @@ [ "_sanity._sanity_test.SanityTest", + "channelz._channelz_servicer_test.ChannelzServicerTest", "health_check._health_servicer_test.HealthServicerTest", "interop._insecure_intraop_test.InsecureIntraopTest", "interop._secure_intraop_test.SecureIntraopTest", diff --git a/templates/src/python/grpcio_channelz/grpc_version.py.template b/templates/src/python/grpcio_channelz/grpc_version.py.template new file mode 100644 index 0000000000..75d510cd17 --- /dev/null +++ b/templates/src/python/grpcio_channelz/grpc_version.py.template @@ -0,0 +1,19 @@ +%YAML 1.2 +--- | + # Copyright 2018 The gRPC Authors + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + + # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio_channelz/grpc_version.py.template`!!! + + VERSION = '${settings.python_version.pep440()}' diff --git a/tools/distrib/pylint_code.sh b/tools/distrib/pylint_code.sh index 82a818cce5..d17eb9fdb8 100755 --- a/tools/distrib/pylint_code.sh +++ b/tools/distrib/pylint_code.sh @@ -20,6 +20,7 @@ cd "$(dirname "$0")/../.." DIRS=( 'src/python/grpcio/grpc' + 'src/python/grpcio_channelz/grpc_channelz' 'src/python/grpcio_health_checking/grpc_health' 'src/python/grpcio_reflection/grpc_reflection' 'src/python/grpcio_testing/grpc_testing' diff --git a/tools/run_tests/artifacts/build_artifact_python.sh b/tools/run_tests/artifacts/build_artifact_python.sh index 65f2d1c765..bc6e558204 100755 --- a/tools/run_tests/artifacts/build_artifact_python.sh +++ b/tools/run_tests/artifacts/build_artifact_python.sh @@ -108,6 +108,11 @@ then ${SETARCH_CMD} "${PYTHON}" src/python/grpcio_testing/setup.py sdist cp -r src/python/grpcio_testing/dist/* "$ARTIFACT_DIR" + # Build grpcio_channelz source distribution + ${SETARCH_CMD} "${PYTHON}" src/python/grpcio_channelz/setup.py \ + preprocess build_package_protos sdist + cp -r src/python/grpcio_channelz/dist/* "$ARTIFACT_DIR" + # Build grpcio_health_checking source distribution ${SETARCH_CMD} "${PYTHON}" src/python/grpcio_health_checking/setup.py \ preprocess build_package_protos sdist diff --git a/tools/run_tests/helper_scripts/build_python.sh b/tools/run_tests/helper_scripts/build_python.sh index 8394f07e51..27acd9664b 100755 --- a/tools/run_tests/helper_scripts/build_python.sh +++ b/tools/run_tests/helper_scripts/build_python.sh @@ -189,6 +189,11 @@ pip_install_dir "$ROOT" $VENV_PYTHON "$ROOT/tools/distrib/python/make_grpcio_tools.py" pip_install_dir "$ROOT/tools/distrib/python/grpcio_tools" +# Build/install Chaneelz +$VENV_PYTHON "$ROOT/src/python/grpcio_channelz/setup.py" preprocess +$VENV_PYTHON "$ROOT/src/python/grpcio_channelz/setup.py" build_package_protos +pip_install_dir "$ROOT/src/python/grpcio_channelz" + # Build/install health checking $VENV_PYTHON "$ROOT/src/python/grpcio_health_checking/setup.py" preprocess $VENV_PYTHON "$ROOT/src/python/grpcio_health_checking/setup.py" build_package_protos -- cgit v1.2.3 From 19bc2c2937b4bd5e77757e6cc157e6345f63df65 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Mon, 26 Nov 2018 11:13:24 +0100 Subject: switch C# docker images to debian stretch --- templates/tools/dockerfile/csharp_deps.include | 25 +++++++------------- .../tools/dockerfile/csharp_dotnetcli_deps.include | 26 +++++++++++++-------- .../grpc_interop_csharp/Dockerfile.template | 2 +- .../grpc_interop_csharpcoreclr/Dockerfile.template | 2 +- .../test/csharp_jessie_x64/Dockerfile.template | 27 ---------------------- .../test/csharp_stretch_x64/Dockerfile.template | 27 ++++++++++++++++++++++ tools/run_tests/run_tests.py | 2 +- 7 files changed, 55 insertions(+), 56 deletions(-) delete mode 100644 templates/tools/dockerfile/test/csharp_jessie_x64/Dockerfile.template create mode 100644 templates/tools/dockerfile/test/csharp_stretch_x64/Dockerfile.template (limited to 'templates') diff --git a/templates/tools/dockerfile/csharp_deps.include b/templates/tools/dockerfile/csharp_deps.include index 7ed0074867..fdede9e840 100644 --- a/templates/tools/dockerfile/csharp_deps.include +++ b/templates/tools/dockerfile/csharp_deps.include @@ -1,24 +1,15 @@ #================ # C# dependencies -# Update to a newer version of mono -RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF -RUN echo "deb http://download.mono-project.com/repo/debian jessie main" | tee /etc/apt/sources.list.d/mono-official.list -RUN echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list -RUN echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list - -# Install dependencies -RUN apt-get update && apt-get -y dist-upgrade && apt-get install -y ${'\\'} +# cmake >=3.6 needed to build grpc_csharp_ext +RUN apt-get update && apt-get install -y cmake && apt-get clean + +# Install mono +RUN apt-get update && apt-get install -y apt-transport-https dirmngr && apt-get clean +RUN apt-key adv --no-tty --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF +RUN echo "deb https://download.mono-project.com/repo/debian stable-stretch main" | tee /etc/apt/sources.list.d/mono-official-stable.list +RUN apt-get update && apt-get install -y ${'\\'} mono-devel ${'\\'} ca-certificates-mono ${'\\'} nuget ${'\\'} && apt-get clean - -RUN nuget update -self - -#================= -# Use cmake 3.6 from jessie-backports -# needed to build grpc_csharp_ext with cmake - -RUN echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/jessie-backports.list -RUN apt-get update && apt-get install -t jessie-backports -y cmake && apt-get clean diff --git a/templates/tools/dockerfile/csharp_dotnetcli_deps.include b/templates/tools/dockerfile/csharp_dotnetcli_deps.include index bc88d2bfa3..f333592640 100644 --- a/templates/tools/dockerfile/csharp_dotnetcli_deps.include +++ b/templates/tools/dockerfile/csharp_dotnetcli_deps.include @@ -1,12 +1,20 @@ -# Install dotnet SDK based on https://www.microsoft.com/net/core#debian -RUN apt-get update && apt-get install -y curl libunwind8 gettext -# dotnet-dev-1.0.0-preview2-003131 -RUN curl -sSL -o dotnet100.tar.gz https://go.microsoft.com/fwlink/?LinkID=827530 -RUN mkdir -p /opt/dotnet && tar zxf dotnet100.tar.gz -C /opt/dotnet -# dotnet-dev-1.0.1 -RUN curl -sSL -o dotnet101.tar.gz https://go.microsoft.com/fwlink/?LinkID=843453 -RUN mkdir -p /opt/dotnet && tar zxf dotnet101.tar.gz -C /opt/dotnet -RUN ln -s /opt/dotnet/dotnet /usr/local/bin +# Install dotnet SDK +ENV DOTNET_SDK_VERSION 2.1.500 +RUN curl -sSL -o dotnet.tar.gz https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz ${'\\'} + && mkdir -p /usr/share/dotnet ${'\\'} + && tar -zxf dotnet.tar.gz -C /usr/share/dotnet ${'\\'} + && rm dotnet.tar.gz ${'\\'} + && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet + + +# Install .NET Core 1.1.10 runtime (required to run netcoreapp1.1) +RUN curl -sSL -o dotnet_old.tar.gz https://dotnetcli.blob.core.windows.net/dotnet/Runtime/1.1.10/dotnet-debian.9-x64.1.1.10.tar.gz ${'\\'} + && mkdir -p dotnet_old ${'\\'} + && tar zxf dotnet_old.tar.gz -C dotnet_old ${'\\'} + && cp -r dotnet_old/shared/Microsoft.NETCore.App/1.1.10/ /usr/share/dotnet/shared/Microsoft.NETCore.App/ ${'\\'} + && rm -rf dotnet_old/ dotnet_old.tar.gz +RUN apt-get update && apt-get install -y libunwind8 && apt-get clean + # Trigger the population of the local package cache ENV NUGET_XMLDOC_MODE skip diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile.template index a1f1283de0..abf1a3853d 100644 --- a/templates/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile.template +++ b/templates/tools/dockerfile/interoptest/grpc_interop_csharp/Dockerfile.template @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - FROM debian:jessie + FROM debian:stretch <%include file="../../apt_get_basic.include"/> <%include file="../../python_deps.include"/> diff --git a/templates/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile.template b/templates/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile.template index a1f1283de0..abf1a3853d 100644 --- a/templates/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile.template +++ b/templates/tools/dockerfile/interoptest/grpc_interop_csharpcoreclr/Dockerfile.template @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. - FROM debian:jessie + FROM debian:stretch <%include file="../../apt_get_basic.include"/> <%include file="../../python_deps.include"/> diff --git a/templates/tools/dockerfile/test/csharp_jessie_x64/Dockerfile.template b/templates/tools/dockerfile/test/csharp_jessie_x64/Dockerfile.template deleted file mode 100644 index 432f05442c..0000000000 --- a/templates/tools/dockerfile/test/csharp_jessie_x64/Dockerfile.template +++ /dev/null @@ -1,27 +0,0 @@ -%YAML 1.2 ---- | - # Copyright 2015 gRPC authors. - # - # Licensed under the Apache License, Version 2.0 (the "License"); - # you may not use this file except in compliance with the License. - # You may obtain a copy of the License at - # - # http://www.apache.org/licenses/LICENSE-2.0 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, - # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - # See the License for the specific language governing permissions and - # limitations under the License. - - FROM debian:jessie - - <%include file="../../apt_get_basic.include"/> - <%include file="../../gcp_api_libraries.include"/> - <%include file="../../python_deps.include"/> - <%include file="../../csharp_deps.include"/> - <%include file="../../csharp_dotnetcli_deps.include"/> - <%include file="../../run_tests_addons.include"/> - # Define the default command. - CMD ["bash"] - diff --git a/templates/tools/dockerfile/test/csharp_stretch_x64/Dockerfile.template b/templates/tools/dockerfile/test/csharp_stretch_x64/Dockerfile.template new file mode 100644 index 0000000000..1526d07fc6 --- /dev/null +++ b/templates/tools/dockerfile/test/csharp_stretch_x64/Dockerfile.template @@ -0,0 +1,27 @@ +%YAML 1.2 +--- | + # Copyright 2018 The gRPC Authors + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + + FROM debian:stretch + + <%include file="../../apt_get_basic.include"/> + <%include file="../../gcp_api_libraries.include"/> + <%include file="../../python_deps.include"/> + <%include file="../../csharp_deps.include"/> + <%include file="../../csharp_dotnetcli_deps.include"/> + <%include file="../../run_tests_addons.include"/> + # Define the default command. + CMD ["bash"] + diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index f71be2a65e..1f756f3c91 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -942,7 +942,7 @@ class CSharpLanguage(object): self._cmake_arch_option = 'x64' else: _check_compiler(self.args.compiler, ['default', 'coreclr']) - self._docker_distro = 'jessie' + self._docker_distro = 'stretch' def test_specs(self): with open('src/csharp/tests.json') as f: -- cgit v1.2.3