From 7942f30f6301f5baf93ea242d13433349640fc57 Mon Sep 17 00:00:00 2001 From: Richard Belleville Date: Fri, 25 Mar 2022 12:01:23 -0700 Subject: [PATCH] Add bazel cpp distribtest for grpc_cc_library (#29175) * Add bazel cpp distribtest for grpc_cc_library * Add new directory to bazelignore * Try changing to workspace differently * Debug cd * Fix bazel wrapper * Yet more debug * Try again * Clean up * Sanity fixes * Run per-language distribtests even if overall build fails --- .bazelignore | 1 + bazel/test/python_test_repo/WORKSPACE | 2 + test/distrib/bazel/cpp/BUILD | 43 ++++++++ test/distrib/bazel/cpp/WORKSPACE | 27 +++++ test/distrib/bazel/cpp/greeter_client.cc | 104 ++++++++++++++++++ test/distrib/bazel/cpp/greeter_server.cc | 73 ++++++++++++ test/distrib/bazel/cpp/greeter_test.sh | 61 ++++++++++ test/distrib/bazel/cpp/protos/BUILD | 37 +++++++ .../distrib/bazel/cpp/protos/helloworld.proto | 38 +++++++ test/distrib/bazel/cpp/tools/bazel | 1 + test/distrib/bazel/run_bazel_distrib_test.sh | 2 +- .../bazel/test_single_bazel_version.sh | 14 ++- 12 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 test/distrib/bazel/cpp/BUILD create mode 100644 test/distrib/bazel/cpp/WORKSPACE create mode 100644 test/distrib/bazel/cpp/greeter_client.cc create mode 100644 test/distrib/bazel/cpp/greeter_server.cc create mode 100755 test/distrib/bazel/cpp/greeter_test.sh create mode 100644 test/distrib/bazel/cpp/protos/BUILD create mode 100644 test/distrib/bazel/cpp/protos/helloworld.proto create mode 120000 test/distrib/bazel/cpp/tools/bazel diff --git a/.bazelignore b/.bazelignore index 160716ea6e8..12c4c1349c6 100644 --- a/.bazelignore +++ b/.bazelignore @@ -21,6 +21,7 @@ third_party/upb third_party/xds bazel/test/python_test_repo +test/distrib/bazel/cpp # Directories generated by setuptools build containing BUILD files. src/python/grpcio_tests/src/ diff --git a/bazel/test/python_test_repo/WORKSPACE b/bazel/test/python_test_repo/WORKSPACE index e704b8b0612..35cb1c8ed6b 100644 --- a/bazel/test/python_test_repo/WORKSPACE +++ b/bazel/test/python_test_repo/WORKSPACE @@ -1,3 +1,5 @@ +# TODO: Move to test/distrib/python. + local_repository( name = "com_github_grpc_grpc", path = "../../..", diff --git a/test/distrib/bazel/cpp/BUILD b/test/distrib/bazel/cpp/BUILD new file mode 100644 index 00000000000..0869e63f20c --- /dev/null +++ b/test/distrib/bazel/cpp/BUILD @@ -0,0 +1,43 @@ +# Copyright 2022 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. + +licenses(["notice"]) + +cc_binary( + name = "greeter_client", + srcs = ["greeter_client.cc"], + deps = [ + "//protos:helloworld_cc_grpc", + "@com_github_grpc_grpc//:grpc++", + ], +) + +cc_binary( + name = "greeter_server", + srcs = ["greeter_server.cc"], + deps = [ + "//protos:helloworld_cc_grpc", + "@com_github_grpc_grpc//:grpc++", + "@com_github_grpc_grpc//:grpc++_reflection", + ], +) + +sh_test( + name = "greeter_test", + srcs = ["greeter_test.sh"], + data = [ + ":greeter_client", + ":greeter_server", + ], +) diff --git a/test/distrib/bazel/cpp/WORKSPACE b/test/distrib/bazel/cpp/WORKSPACE new file mode 100644 index 00000000000..4b7644085ee --- /dev/null +++ b/test/distrib/bazel/cpp/WORKSPACE @@ -0,0 +1,27 @@ +# Copyright 2022 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. +local_repository( + name = "com_github_grpc_grpc", + path = "../../../..", +) + +workspace(name = "bazel_cpp_distribtests") + +load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") + +grpc_deps() + +load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps") + +grpc_extra_deps() diff --git a/test/distrib/bazel/cpp/greeter_client.cc b/test/distrib/bazel/cpp/greeter_client.cc new file mode 100644 index 00000000000..75597c1f21e --- /dev/null +++ b/test/distrib/bazel/cpp/greeter_client.cc @@ -0,0 +1,104 @@ +/* + * + * Copyright 2022 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. + * + */ + +#include +#include +#include + +#include + +#include "protos/helloworld.grpc.pb.h" + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; +using helloworld::Greeter; +using helloworld::HelloReply; +using helloworld::HelloRequest; + +class GreeterClient { + public: + GreeterClient(std::shared_ptr channel) + : stub_(Greeter::NewStub(channel)) {} + + // Assembles the client's payload, sends it and presents the response back + // from the server. + std::string SayHello(const std::string& user) { + // Data we are sending to the server. + HelloRequest request; + request.set_name(user); + + // Container for the data we expect from the server. + HelloReply reply; + + // Context for the client. It could be used to convey extra information to + // the server and/or tweak certain RPC behaviors. + ClientContext context; + + // The actual RPC. + Status status = stub_->SayHello(&context, request, &reply); + + // Act upon its status. + if (status.ok()) { + return reply.message(); + } else { + std::cout << status.error_code() << ": " << status.error_message() + << std::endl; + return "RPC failed"; + } + } + + private: + std::unique_ptr stub_; +}; + +int main(int argc, char** argv) { + // Instantiate the client. It requires a channel, out of which the actual RPCs + // are created. This channel models a connection to an endpoint specified by + // the argument "--target=" which is the only expected argument. + // We indicate that the channel isn't authenticated (use of + // InsecureChannelCredentials()). + std::string target_str; + std::string arg_str("--target"); + if (argc > 1) { + std::string arg_val = argv[1]; + size_t start_pos = arg_val.find(arg_str); + if (start_pos != std::string::npos) { + start_pos += arg_str.size(); + if (arg_val[start_pos] == '=') { + target_str = arg_val.substr(start_pos + 1); + } else { + std::cout << "The only correct argument syntax is --target=" + << std::endl; + return 0; + } + } else { + std::cout << "The only acceptable argument is --target=" << std::endl; + return 0; + } + } else { + target_str = "localhost:50051"; + } + GreeterClient greeter( + grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials())); + std::string user("world"); + std::string reply = greeter.SayHello(user); + std::cout << "Greeter received: " << reply << std::endl; + + return 0; +} diff --git a/test/distrib/bazel/cpp/greeter_server.cc b/test/distrib/bazel/cpp/greeter_server.cc new file mode 100644 index 00000000000..f42c9b8bdf5 --- /dev/null +++ b/test/distrib/bazel/cpp/greeter_server.cc @@ -0,0 +1,73 @@ +/* + * + * Copyright 2022 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. + * + */ + +#include +#include +#include + +#include +#include +#include + +#include "protos/helloworld.grpc.pb.h" + +using grpc::Server; +using grpc::ServerBuilder; +using grpc::ServerContext; +using grpc::Status; +using helloworld::Greeter; +using helloworld::HelloReply; +using helloworld::HelloRequest; + +// Logic and data behind the server's behavior. +class GreeterServiceImpl final : public Greeter::Service { + Status SayHello(ServerContext* context, const HelloRequest* request, + HelloReply* reply) override { + std::string prefix("Hello "); + reply->set_message(prefix + request->name()); + return Status::OK; + } +}; + +void RunServer() { + std::string server_address("0.0.0.0:0"); + GreeterServiceImpl service; + + grpc::EnableDefaultHealthCheckService(true); + grpc::reflection::InitProtoReflectionServerBuilderPlugin(); + ServerBuilder builder; + // Listen on the given address without any authentication mechanism. + int bound_port; + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials(), &bound_port); + // Register "service" as the instance through which we'll communicate with + // clients. In this case it corresponds to an *synchronous* service. + builder.RegisterService(&service); + // Finally assemble the server. + std::unique_ptr server(builder.BuildAndStart()); + std::cout << "127.0.0.1:" << bound_port << std::endl; + + // Wait for the server to shutdown. Note that some other thread must be + // responsible for shutting down the server for this call to ever return. + server->Wait(); +} + +int main(int argc, char** argv) { + RunServer(); + + return 0; +} diff --git a/test/distrib/bazel/cpp/greeter_test.sh b/test/distrib/bazel/cpp/greeter_test.sh new file mode 100755 index 00000000000..92f6233e6ae --- /dev/null +++ b/test/distrib/bazel/cpp/greeter_test.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# Copyright 2022 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. + +set -exo pipefail + +SERVER_PID="" +SERVER_TIMEOUT=10 + +SERVER_OUTPUT=$(mktemp) + +function cleanup() { + if [ -n "$SERVER_PID" ]; then + kill "$SERVER_PID" + fi +} + +function fail() { + echo "$1" >/dev/stderr + echo "Failed." >/dev/stderr + exit 1 +} + +function await_server() { + TIME=0 + while [ ! -s "$SERVER_OUTPUT" ]; do + if [ "$TIME" == "$SERVER_TIMEOUT" ] ; then + fail "Server not listening after $SERVER_TIMEOUT seconds." + fi + sleep 1 + TIME=$((TIME+1)) + done + cat "$SERVER_OUTPUT" +} + +trap cleanup SIGINT SIGTERM EXIT + +./greeter_server >"$SERVER_OUTPUT" & +SERVER_PID=$! + +SERVER_ADDRESS=$(await_server) + +RESPONSE=$(./greeter_client --target="$SERVER_ADDRESS") +EXPECTED_RESPONSE="Greeter received: Hello world" + +if [ "$RESPONSE" != "$EXPECTED_RESPONSE" ]; then + fail "Received response \"$RESPONSE\" but expected \"$EXPECTED_RESPONSE\"" +fi + +echo "Success." diff --git a/test/distrib/bazel/cpp/protos/BUILD b/test/distrib/bazel/cpp/protos/BUILD new file mode 100644 index 00000000000..5923542b55c --- /dev/null +++ b/test/distrib/bazel/cpp/protos/BUILD @@ -0,0 +1,37 @@ +# Copyright 2022 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. + +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") + +licenses(["notice"]) + +package(default_visibility = ["//:__subpackages__"]) + +proto_library( + name = "helloworld_proto", + srcs = ["helloworld.proto"], +) + +cc_proto_library( + name = "helloworld_cc_proto", + deps = [":helloworld_proto"], +) + +cc_grpc_library( + name = "helloworld_cc_grpc", + srcs = [":helloworld_proto"], + grpc_only = True, + deps = [":helloworld_cc_proto"], +) diff --git a/test/distrib/bazel/cpp/protos/helloworld.proto b/test/distrib/bazel/cpp/protos/helloworld.proto new file mode 100644 index 00000000000..be878ce25ff --- /dev/null +++ b/test/distrib/bazel/cpp/protos/helloworld.proto @@ -0,0 +1,38 @@ +// 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. + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; +option objc_class_prefix = "HLW"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/test/distrib/bazel/cpp/tools/bazel b/test/distrib/bazel/cpp/tools/bazel new file mode 120000 index 00000000000..e50c110d3a8 --- /dev/null +++ b/test/distrib/bazel/cpp/tools/bazel @@ -0,0 +1 @@ +../../../../../tools/bazel \ No newline at end of file diff --git a/test/distrib/bazel/run_bazel_distrib_test.sh b/test/distrib/bazel/run_bazel_distrib_test.sh index d9deff289a8..347417a7bd1 100755 --- a/test/distrib/bazel/run_bazel_distrib_test.sh +++ b/test/distrib/bazel/run_bazel_distrib_test.sh @@ -28,6 +28,6 @@ done if [ "$FAILED_VERSIONS" != "" ] then - echo "Bazel distribtest failed: Failed to build with bazel versions ${FAILED_VERSIONS}" + echo "Bazel distribtest failed: Failing versions: ${FAILED_VERSIONS}" exit 1 fi diff --git a/test/distrib/bazel/test_single_bazel_version.sh b/test/distrib/bazel/test_single_bazel_version.sh index ef338c9349b..0fa68425411 100755 --- a/test/distrib/bazel/test_single_bazel_version.sh +++ b/test/distrib/bazel/test_single_bazel_version.sh @@ -41,7 +41,19 @@ EXCLUDED_TARGETS=( "-//examples/android/binder/..." ) +FAILED_TESTS="" + export OVERRIDE_BAZEL_VERSION="$VERSION" # when running under bazel docker image, the workspace is read only. export OVERRIDE_BAZEL_WRAPPER_DOWNLOAD_DIR=/tmp -bazel build -- //... "${EXCLUDED_TARGETS[@]}" +bazel build -- //... "${EXCLUDED_TARGETS[@]}" || FAILED_TESTS="${FAILED_TESTS}Build " + +cd test/distrib/bazel/cpp/ + +bazel test //:all || FAILED_TESTS="${FAILED_TESTS}C++ Distribtest" + +if [ "$FAILED_TESTS" != "" ] +then + echo "Failed tests at version ${VERSION}: ${FAILED_TESTS}" + exit 1 +fi