tonic-health: commit generated code (#1065)

* tonic-health: commit generated code rather than generating at build time

This saves users from needing protoc available on their path if they're not
generating other tonic code at build time, either.

The generated modules can be regenerated by enabling the `gen-proto`
feature, which will trigger the relevant part of the build script.

* Check generated code for tonic-health matches in CI

* Use bootstrap test to generate/check validity of generated code

As suggested by @LucioFranco in https://github.com/hyperium/tonic/pull/1065\#discussion_r951580189.
This avoids the need for a feature which would show
up in docs.rs, and achieves the same goals via CI.
This commit is contained in:
Ben Sully
2022-08-23 15:31:37 +01:00
committed by GitHub
parent 2d66a0aa30
commit 0a2a2f3ac5
6 changed files with 413 additions and 20 deletions
+1 -3
View File
@@ -16,7 +16,7 @@ version = "0.7.0"
[features]
default = ["transport"]
transport = ["tonic/transport", "tonic-build/transport"]
transport = ["tonic/transport"]
[dependencies]
async-stream = "0.3"
@@ -28,6 +28,4 @@ tonic = {version = "0.8", path = "../tonic", features = ["codegen", "prost"]}
[dev-dependencies]
tokio = {version = "1.0", features = ["rt-multi-thread", "macros"]}
[build-dependencies]
tonic-build = {version = "0.8", path = "../tonic-build", features = ["prost"]}
-14
View File
@@ -1,14 +0,0 @@
use std::env;
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let grpc_health_v1_descriptor_set_path: PathBuf =
PathBuf::from(env::var("OUT_DIR").unwrap()).join("grpc_health_v1.bin");
tonic_build::configure()
.file_descriptor_set_path(grpc_health_v1_descriptor_set_path)
.build_server(true)
.build_client(true)
.compile(&["proto/health.proto"], &["proto/"])?;
Ok(())
}
@@ -0,0 +1,379 @@
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct HealthCheckRequest {
#[prost(string, tag="1")]
pub service: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct HealthCheckResponse {
#[prost(enumeration="health_check_response::ServingStatus", tag="1")]
pub status: i32,
}
/// Nested message and enum types in `HealthCheckResponse`.
pub mod health_check_response {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ServingStatus {
Unknown = 0,
Serving = 1,
NotServing = 2,
/// Used only by the Watch method.
ServiceUnknown = 3,
}
impl ServingStatus {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
ServingStatus::Unknown => "UNKNOWN",
ServingStatus::Serving => "SERVING",
ServingStatus::NotServing => "NOT_SERVING",
ServingStatus::ServiceUnknown => "SERVICE_UNKNOWN",
}
}
}
}
/// Generated client implementations.
pub mod health_client {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::*;
use tonic::codegen::http::Uri;
#[derive(Debug, Clone)]
pub struct HealthClient<T> {
inner: tonic::client::Grpc<T>,
}
impl HealthClient<tonic::transport::Channel> {
/// Attempt to create a new client by connecting to a given endpoint.
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
where
D: std::convert::TryInto<tonic::transport::Endpoint>,
D::Error: Into<StdError>,
{
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
Ok(Self::new(conn))
}
}
impl<T> HealthClient<T>
where
T: tonic::client::GrpcService<tonic::body::BoxBody>,
T::Error: Into<StdError>,
T::ResponseBody: Body<Data = Bytes> + Send + 'static,
<T::ResponseBody as Body>::Error: Into<StdError> + Send,
{
pub fn new(inner: T) -> Self {
let inner = tonic::client::Grpc::new(inner);
Self { inner }
}
pub fn with_origin(inner: T, origin: Uri) -> Self {
let inner = tonic::client::Grpc::with_origin(inner, origin);
Self { inner }
}
pub fn with_interceptor<F>(
inner: T,
interceptor: F,
) -> HealthClient<InterceptedService<T, F>>
where
F: tonic::service::Interceptor,
T::ResponseBody: Default,
T: tonic::codegen::Service<
http::Request<tonic::body::BoxBody>,
Response = http::Response<
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
>,
>,
<T as tonic::codegen::Service<
http::Request<tonic::body::BoxBody>,
>>::Error: Into<StdError> + Send + Sync,
{
HealthClient::new(InterceptedService::new(inner, interceptor))
}
/// Compress requests with the given encoding.
///
/// This requires the server to support it otherwise it might respond with an
/// error.
#[must_use]
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.inner = self.inner.send_compressed(encoding);
self
}
/// Enable decompressing responses.
#[must_use]
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.inner = self.inner.accept_compressed(encoding);
self
}
/// If the requested service is unknown, the call will fail with status
/// NOT_FOUND.
pub async fn check(
&mut self,
request: impl tonic::IntoRequest<super::HealthCheckRequest>,
) -> Result<tonic::Response<super::HealthCheckResponse>, tonic::Status> {
self.inner
.ready()
.await
.map_err(|e| {
tonic::Status::new(
tonic::Code::Unknown,
format!("Service was not ready: {}", e.into()),
)
})?;
let codec = tonic::codec::ProstCodec::default();
let path = http::uri::PathAndQuery::from_static(
"/grpc.health.v1.Health/Check",
);
self.inner.unary(request.into_request(), path, codec).await
}
/// Performs a watch for the serving status of the requested service.
/// The server will immediately send back a message indicating the current
/// serving status. It will then subsequently send a new message whenever
/// the service's serving status changes.
///
/// If the requested service is unknown when the call is received, the
/// server will send a message setting the serving status to
/// SERVICE_UNKNOWN but will *not* terminate the call. If at some
/// future point, the serving status of the service becomes known, the
/// server will send a new message with the service's serving status.
///
/// If the call terminates with status UNIMPLEMENTED, then clients
/// should assume this method is not supported and should not retry the
/// call. If the call terminates with any other status (including OK),
/// clients should retry the call with appropriate exponential backoff.
pub async fn watch(
&mut self,
request: impl tonic::IntoRequest<super::HealthCheckRequest>,
) -> Result<
tonic::Response<tonic::codec::Streaming<super::HealthCheckResponse>>,
tonic::Status,
> {
self.inner
.ready()
.await
.map_err(|e| {
tonic::Status::new(
tonic::Code::Unknown,
format!("Service was not ready: {}", e.into()),
)
})?;
let codec = tonic::codec::ProstCodec::default();
let path = http::uri::PathAndQuery::from_static(
"/grpc.health.v1.Health/Watch",
);
self.inner.server_streaming(request.into_request(), path, codec).await
}
}
}
/// Generated server implementations.
pub mod health_server {
#![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)]
use tonic::codegen::*;
///Generated trait containing gRPC methods that should be implemented for use with HealthServer.
#[async_trait]
pub trait Health: Send + Sync + 'static {
/// If the requested service is unknown, the call will fail with status
/// NOT_FOUND.
async fn check(
&self,
request: tonic::Request<super::HealthCheckRequest>,
) -> Result<tonic::Response<super::HealthCheckResponse>, tonic::Status>;
///Server streaming response type for the Watch method.
type WatchStream: futures_core::Stream<
Item = Result<super::HealthCheckResponse, tonic::Status>,
>
+ Send
+ 'static;
/// Performs a watch for the serving status of the requested service.
/// The server will immediately send back a message indicating the current
/// serving status. It will then subsequently send a new message whenever
/// the service's serving status changes.
///
/// If the requested service is unknown when the call is received, the
/// server will send a message setting the serving status to
/// SERVICE_UNKNOWN but will *not* terminate the call. If at some
/// future point, the serving status of the service becomes known, the
/// server will send a new message with the service's serving status.
///
/// If the call terminates with status UNIMPLEMENTED, then clients
/// should assume this method is not supported and should not retry the
/// call. If the call terminates with any other status (including OK),
/// clients should retry the call with appropriate exponential backoff.
async fn watch(
&self,
request: tonic::Request<super::HealthCheckRequest>,
) -> Result<tonic::Response<Self::WatchStream>, tonic::Status>;
}
#[derive(Debug)]
pub struct HealthServer<T: Health> {
inner: _Inner<T>,
accept_compression_encodings: EnabledCompressionEncodings,
send_compression_encodings: EnabledCompressionEncodings,
}
struct _Inner<T>(Arc<T>);
impl<T: Health> HealthServer<T> {
pub fn new(inner: T) -> Self {
Self::from_arc(Arc::new(inner))
}
pub fn from_arc(inner: Arc<T>) -> Self {
let inner = _Inner(inner);
Self {
inner,
accept_compression_encodings: Default::default(),
send_compression_encodings: Default::default(),
}
}
pub fn with_interceptor<F>(
inner: T,
interceptor: F,
) -> InterceptedService<Self, F>
where
F: tonic::service::Interceptor,
{
InterceptedService::new(Self::new(inner), interceptor)
}
/// Enable decompressing requests with the given encoding.
#[must_use]
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.accept_compression_encodings.enable(encoding);
self
}
/// Compress responses with the given encoding, if the client supports it.
#[must_use]
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
self.send_compression_encodings.enable(encoding);
self
}
}
impl<T, B> tonic::codegen::Service<http::Request<B>> for HealthServer<T>
where
T: Health,
B: Body + Send + 'static,
B::Error: Into<StdError> + Send + 'static,
{
type Response = http::Response<tonic::body::BoxBody>;
type Error = std::convert::Infallible;
type Future = BoxFuture<Self::Response, Self::Error>;
fn poll_ready(
&mut self,
_cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: http::Request<B>) -> Self::Future {
let inner = self.inner.clone();
match req.uri().path() {
"/grpc.health.v1.Health/Check" => {
#[allow(non_camel_case_types)]
struct CheckSvc<T: Health>(pub Arc<T>);
impl<
T: Health,
> tonic::server::UnaryService<super::HealthCheckRequest>
for CheckSvc<T> {
type Response = super::HealthCheckResponse;
type Future = BoxFuture<
tonic::Response<Self::Response>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<super::HealthCheckRequest>,
) -> Self::Future {
let inner = self.0.clone();
let fut = async move { (*inner).check(request).await };
Box::pin(fut)
}
}
let accept_compression_encodings = self.accept_compression_encodings;
let send_compression_encodings = self.send_compression_encodings;
let inner = self.inner.clone();
let fut = async move {
let inner = inner.0;
let method = CheckSvc(inner);
let codec = tonic::codec::ProstCodec::default();
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(
accept_compression_encodings,
send_compression_encodings,
);
let res = grpc.unary(method, req).await;
Ok(res)
};
Box::pin(fut)
}
"/grpc.health.v1.Health/Watch" => {
#[allow(non_camel_case_types)]
struct WatchSvc<T: Health>(pub Arc<T>);
impl<
T: Health,
> tonic::server::ServerStreamingService<super::HealthCheckRequest>
for WatchSvc<T> {
type Response = super::HealthCheckResponse;
type ResponseStream = T::WatchStream;
type Future = BoxFuture<
tonic::Response<Self::ResponseStream>,
tonic::Status,
>;
fn call(
&mut self,
request: tonic::Request<super::HealthCheckRequest>,
) -> Self::Future {
let inner = self.0.clone();
let fut = async move { (*inner).watch(request).await };
Box::pin(fut)
}
}
let accept_compression_encodings = self.accept_compression_encodings;
let send_compression_encodings = self.send_compression_encodings;
let inner = self.inner.clone();
let fut = async move {
let inner = inner.0;
let method = WatchSvc(inner);
let codec = tonic::codec::ProstCodec::default();
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(
accept_compression_encodings,
send_compression_encodings,
);
let res = grpc.server_streaming(method, req).await;
Ok(res)
};
Box::pin(fut)
}
_ => {
Box::pin(async move {
Ok(
http::Response::builder()
.status(200)
.header("grpc-status", "12")
.header("content-type", "application/grpc")
.body(empty_body())
.unwrap(),
)
})
}
}
}
}
impl<T: Health> Clone for HealthServer<T> {
fn clone(&self) -> Self {
let inner = self.inner.clone();
Self {
inner,
accept_compression_encodings: self.accept_compression_encodings,
send_compression_encodings: self.send_compression_encodings,
}
}
}
impl<T: Health> Clone for _Inner<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for _Inner<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl<T: Health> tonic::server::NamedService for HealthServer<T> {
const NAME: &'static str = "grpc.health.v1.Health";
}
}
Binary file not shown.
+3 -3
View File
@@ -23,14 +23,14 @@
use std::fmt::{Display, Formatter};
/// Generated protobuf types from the `grpc.healthy.v1` package.
/// Generated protobuf types from the `grpc.health.v1` package.
pub mod proto {
#![allow(unreachable_pub)]
#![allow(missing_docs)]
tonic::include_proto!("grpc.health.v1");
include!("generated/grpc.health.v1.rs");
pub const GRPC_HEALTH_V1_FILE_DESCRIPTOR_SET: &[u8] =
tonic::include_file_descriptor_set!("grpc_health_v1");
include_bytes!("generated/grpc_health_v1.bin");
}
pub mod server;
+30
View File
@@ -0,0 +1,30 @@
use std::{path::PathBuf, process::Command};
#[test]
fn bootstrap() {
let iface_files = &["proto/health.proto"];
let dirs = &["proto"];
let out_dir = PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("generated");
tonic_build::configure()
.build_client(true)
.build_server(true)
.out_dir(format!("{}", out_dir.display()))
.compile(iface_files, dirs)
.unwrap();
let status = Command::new("git")
.arg("diff")
.arg("--exit-code")
.arg("--")
.arg(format!("{}", out_dir.display()))
.status()
.unwrap();
if !status.success() {
panic!("You should commit the protobuf files");
}
}