Skip to content

Commit 39fc083

Browse files
authored
feat(vpnd): add grpc health and reflection (#299)
1 parent 8f62866 commit 39fc083

File tree

9 files changed

+140
-31
lines changed

9 files changed

+140
-31
lines changed

Cargo.lock

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ tokio-util = { version = "0.7.10", features = ["codec"] }
7171
toml = "0.8.12"
7272
tonic = "0.11.0"
7373
tonic-build = "0.11.0"
74+
tonic-health = "0.11.0"
75+
tonic-reflection = { version = "0.11.0", features = ["server"] }
7476
tower = "0.4.13"
7577
tracing = "0.1"
7678
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

crates/nym-vpn-proto/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ license.workspace = true
1010

1111
[dependencies]
1212
tonic.workspace = true
13+
tonic-reflection.workspace = true
1314
prost.workspace = true
1415

1516
[build-dependencies]

crates/nym-vpn-proto/build.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
// Copyright 2024 - Nym Technologies SA <[email protected]>
22
// SPDX-License-Identifier: Apache-2.0
33

4+
use std::{env, path::PathBuf};
5+
46
fn main() -> Result<(), Box<dyn std::error::Error>> {
5-
tonic_build::compile_protos("../../proto/nym/vpn.proto")?;
6-
tonic_build::compile_protos("../../proto/nym/health.proto")?;
7+
// needed for reflection
8+
let descriptor_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("vpn_descriptor.bin");
9+
10+
tonic_build::configure()
11+
.file_descriptor_set_path(descriptor_path)
12+
.compile(&["../../proto/nym/vpn.proto"], &["../../proto/nym/"])?;
713
Ok(())
814
}

crates/nym-vpn-proto/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
tonic::include_proto!("nym.vpn");
2-
tonic::include_proto!("nym.health");
2+
3+
pub const FILE_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("vpn_descriptor");

nym-vpnd/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ toml.workspace = true
2020
tonic.workspace = true
2121
tracing-subscriber = { workspace = true, features = ["env-filter"] }
2222
tracing.workspace = true
23+
tonic-health.workspace = true
24+
tonic-reflection.workspace = true
2325

2426
nym-vpn-lib = { path = "../nym-vpn-lib" }
2527
nym-vpn-proto = { path = "../crates/nym-vpn-proto" }

nym-vpnd/src/command_interface/start.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use std::{net::SocketAddr, path::PathBuf};
55

66
use nym_task::TaskManager;
7-
use nym_vpn_proto::nym_vpnd_server::NymVpndServer;
7+
use nym_vpn_proto::{nym_vpnd_server::NymVpndServer, FILE_DESCRIPTOR_SET};
88
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
99
use tonic::transport::Server;
1010
use tracing::info;
@@ -19,8 +19,19 @@ use crate::{cli::CliArgs, service::VpnServiceCommand};
1919
fn spawn_uri_listener(vpn_command_tx: UnboundedSender<VpnServiceCommand>, addr: SocketAddr) {
2020
info!("Starting HTTP listener on: {addr}");
2121
tokio::task::spawn(async move {
22+
let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
23+
health_reporter
24+
.set_serving::<NymVpndServer<CommandInterface>>()
25+
.await;
26+
let reflection_service = tonic_reflection::server::Builder::configure()
27+
.register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
28+
.build()
29+
.unwrap();
2230
let command_interface = CommandInterface::new_with_uri(vpn_command_tx, addr);
31+
2332
Server::builder()
33+
.add_service(health_service)
34+
.add_service(reflection_service)
2435
.add_service(NymVpndServer::new(command_interface))
2536
.serve(addr)
2637
.await
@@ -31,10 +42,21 @@ fn spawn_uri_listener(vpn_command_tx: UnboundedSender<VpnServiceCommand>, addr:
3142
fn spawn_socket_listener(vpn_command_tx: UnboundedSender<VpnServiceCommand>, socket_path: PathBuf) {
3243
info!("Starting socket listener on: {}", socket_path.display());
3344
tokio::task::spawn(async move {
45+
let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
46+
health_reporter
47+
.set_serving::<NymVpndServer<CommandInterface>>()
48+
.await;
49+
let reflection_service = tonic_reflection::server::Builder::configure()
50+
.register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
51+
.build()
52+
.unwrap();
3453
let command_interface = CommandInterface::new_with_path(vpn_command_tx, &socket_path);
3554
command_interface.remove_previous_socket_file();
3655
let incoming = setup_socket_stream(&socket_path);
56+
3757
Server::builder()
58+
.add_service(health_service)
59+
.add_service(reflection_service)
3860
.add_service(NymVpndServer::new(command_interface))
3961
.serve_with_incoming(incoming)
4062
.await

proto/grpc/health.proto

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2015 The gRPC Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// The canonical version of this proto can be found at
16+
// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
17+
18+
syntax = "proto3";
19+
20+
package grpc.health.v1;
21+
22+
option csharp_namespace = "Grpc.Health.V1";
23+
option go_package = "google.golang.org/grpc/health/grpc_health_v1";
24+
option java_multiple_files = true;
25+
option java_outer_classname = "HealthProto";
26+
option java_package = "io.grpc.health.v1";
27+
28+
message HealthCheckRequest {
29+
string service = 1;
30+
}
31+
32+
message HealthCheckResponse {
33+
enum ServingStatus {
34+
UNKNOWN = 0;
35+
SERVING = 1;
36+
NOT_SERVING = 2;
37+
SERVICE_UNKNOWN = 3; // Used only by the Watch method.
38+
}
39+
ServingStatus status = 1;
40+
}
41+
42+
// Health is gRPC's mechanism for checking whether a server is able to handle
43+
// RPCs. Its semantics are documented in
44+
// https://github.com/grpc/grpc/blob/master/doc/health-checking.md.
45+
service Health {
46+
// Check gets the health of the specified service. If the requested service
47+
// is unknown, the call will fail with status NOT_FOUND. If the caller does
48+
// not specify a service name, the server should respond with its overall
49+
// health status.
50+
//
51+
// Clients should set a deadline when calling Check, and can declare the
52+
// server unhealthy if they do not receive a timely response.
53+
//
54+
// Check implementations should be idempotent and side effect free.
55+
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
56+
57+
// Performs a watch for the serving status of the requested service.
58+
// The server will immediately send back a message indicating the current
59+
// serving status. It will then subsequently send a new message whenever
60+
// the service's serving status changes.
61+
//
62+
// If the requested service is unknown when the call is received, the
63+
// server will send a message setting the serving status to
64+
// SERVICE_UNKNOWN but will *not* terminate the call. If at some
65+
// future point, the serving status of the service becomes known, the
66+
// server will send a new message with the service's serving status.
67+
//
68+
// If the call terminates with status UNIMPLEMENTED, then clients
69+
// should assume this method is not supported and should not retry the
70+
// call. If the call terminates with any other status (including OK),
71+
// clients should retry the call with appropriate exponential backoff.
72+
rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
73+
}

proto/nym/health.proto

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)