Skip to content

Commit

Permalink
feat(vpnd): add grpc health and reflection (#299)
Browse files Browse the repository at this point in the history
  • Loading branch information
doums authored Apr 24, 2024
1 parent 8f62866 commit 39fc083
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 31 deletions.
29 changes: 29 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ tokio-util = { version = "0.7.10", features = ["codec"] }
toml = "0.8.12"
tonic = "0.11.0"
tonic-build = "0.11.0"
tonic-health = "0.11.0"
tonic-reflection = { version = "0.11.0", features = ["server"] }
tower = "0.4.13"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
Expand Down
1 change: 1 addition & 0 deletions crates/nym-vpn-proto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ license.workspace = true

[dependencies]
tonic.workspace = true
tonic-reflection.workspace = true
prost.workspace = true

[build-dependencies]
Expand Down
10 changes: 8 additions & 2 deletions crates/nym-vpn-proto/build.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
// Copyright 2024 - Nym Technologies SA <[email protected]>
// SPDX-License-Identifier: Apache-2.0

use std::{env, path::PathBuf};

fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("../../proto/nym/vpn.proto")?;
tonic_build::compile_protos("../../proto/nym/health.proto")?;
// needed for reflection
let descriptor_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("vpn_descriptor.bin");

tonic_build::configure()
.file_descriptor_set_path(descriptor_path)
.compile(&["../../proto/nym/vpn.proto"], &["../../proto/nym/"])?;
Ok(())
}
3 changes: 2 additions & 1 deletion crates/nym-vpn-proto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
tonic::include_proto!("nym.vpn");
tonic::include_proto!("nym.health");

pub const FILE_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("vpn_descriptor");
2 changes: 2 additions & 0 deletions nym-vpnd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ toml.workspace = true
tonic.workspace = true
tracing-subscriber = { workspace = true, features = ["env-filter"] }
tracing.workspace = true
tonic-health.workspace = true
tonic-reflection.workspace = true

nym-vpn-lib = { path = "../nym-vpn-lib" }
nym-vpn-proto = { path = "../crates/nym-vpn-proto" }
Expand Down
24 changes: 23 additions & 1 deletion nym-vpnd/src/command_interface/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use std::{net::SocketAddr, path::PathBuf};

use nym_task::TaskManager;
use nym_vpn_proto::nym_vpnd_server::NymVpndServer;
use nym_vpn_proto::{nym_vpnd_server::NymVpndServer, FILE_DESCRIPTOR_SET};
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use tonic::transport::Server;
use tracing::info;
Expand All @@ -19,8 +19,19 @@ use crate::{cli::CliArgs, service::VpnServiceCommand};
fn spawn_uri_listener(vpn_command_tx: UnboundedSender<VpnServiceCommand>, addr: SocketAddr) {
info!("Starting HTTP listener on: {addr}");
tokio::task::spawn(async move {
let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
health_reporter
.set_serving::<NymVpndServer<CommandInterface>>()
.await;
let reflection_service = tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
.build()
.unwrap();
let command_interface = CommandInterface::new_with_uri(vpn_command_tx, addr);

Server::builder()
.add_service(health_service)
.add_service(reflection_service)
.add_service(NymVpndServer::new(command_interface))
.serve(addr)
.await
Expand All @@ -31,10 +42,21 @@ fn spawn_uri_listener(vpn_command_tx: UnboundedSender<VpnServiceCommand>, addr:
fn spawn_socket_listener(vpn_command_tx: UnboundedSender<VpnServiceCommand>, socket_path: PathBuf) {
info!("Starting socket listener on: {}", socket_path.display());
tokio::task::spawn(async move {
let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
health_reporter
.set_serving::<NymVpndServer<CommandInterface>>()
.await;
let reflection_service = tonic_reflection::server::Builder::configure()
.register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
.build()
.unwrap();
let command_interface = CommandInterface::new_with_path(vpn_command_tx, &socket_path);
command_interface.remove_previous_socket_file();
let incoming = setup_socket_stream(&socket_path);

Server::builder()
.add_service(health_service)
.add_service(reflection_service)
.add_service(NymVpndServer::new(command_interface))
.serve_with_incoming(incoming)
.await
Expand Down
73 changes: 73 additions & 0 deletions proto/grpc/health.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2015 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.

// The canonical version of this proto can be found at
// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto

syntax = "proto3";

package grpc.health.v1;

option csharp_namespace = "Grpc.Health.V1";
option go_package = "google.golang.org/grpc/health/grpc_health_v1";
option java_multiple_files = true;
option java_outer_classname = "HealthProto";
option java_package = "io.grpc.health.v1";

message HealthCheckRequest {
string service = 1;
}

message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
SERVICE_UNKNOWN = 3; // Used only by the Watch method.
}
ServingStatus status = 1;
}

// Health is gRPC's mechanism for checking whether a server is able to handle
// RPCs. Its semantics are documented in
// https://github.com/grpc/grpc/blob/master/doc/health-checking.md.
service Health {
// Check gets the health of the specified service. If the requested service
// is unknown, the call will fail with status NOT_FOUND. If the caller does
// not specify a service name, the server should respond with its overall
// health status.
//
// Clients should set a deadline when calling Check, and can declare the
// server unhealthy if they do not receive a timely response.
//
// Check implementations should be idempotent and side effect free.
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);

// 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.
rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}
27 changes: 0 additions & 27 deletions proto/nym/health.proto

This file was deleted.

0 comments on commit 39fc083

Please sign in to comment.