cli: add tunnel status command (#177381)

Fixes #177372
This commit is contained in:
Connor Peet 2023-03-16 12:26:56 -07:00 committed by GitHub
parent 7c1b8f38a4
commit eab8ba65bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 57 additions and 25 deletions

View file

@ -93,6 +93,7 @@ async fn main() -> Result<(), std::convert::Infallible> {
Some(args::TunnelSubcommand::Unregister) => tunnels::unregister(context).await,
Some(args::TunnelSubcommand::Kill) => tunnels::kill(context).await,
Some(args::TunnelSubcommand::Restart) => tunnels::restart(context).await,
Some(args::TunnelSubcommand::Status) => tunnels::status(context).await,
Some(args::TunnelSubcommand::Rename(rename_args)) => {
tunnels::rename(context, rename_args).await
}

View file

@ -630,6 +630,9 @@ pub enum TunnelSubcommand {
/// Restarts any running tunnel on the system.
Restart,
/// Gets whether there is a tunnel running on the current machineiou.
Status,
/// Rename the name of this machine associated with port forwarding service.
Rename(TunnelRenameArgs),

View file

@ -203,17 +203,19 @@ pub async fn unregister(ctx: CommandContext) -> Result<i32, AnyError> {
Ok(0)
}
async fn do_single_rpc_call(
ctx: CommandContext,
async fn do_single_rpc_call<
P: serde::Serialize,
R: serde::de::DeserializeOwned + Send + 'static,
>(
ctx: &CommandContext,
method: &'static str,
params: impl serde::Serialize,
) -> Result<i32, AnyError> {
params: P,
) -> Result<R, AnyError> {
let client = match connect_as_client(&ctx.paths.tunnel_lockfile()).await {
Ok(p) => p,
Err(CodeError::SingletonLockfileOpenFailed(_))
| Err(CodeError::SingletonLockedProcessExited(_)) => {
error!(ctx.log, "No tunnel is running");
return Ok(1);
return Err(CodeError::NoRunningTunnel.into());
}
Err(e) => return Err(e.into()),
};
@ -236,33 +238,42 @@ async fn do_single_rpc_call(
.unwrap();
});
let r = caller.call::<_, _, ()>(method, params).await.unwrap();
let r = caller.call(method, params).await.unwrap();
rpc.abort();
if let Err(r) = r {
error!(ctx.log, "RPC call failed: {:?}", r);
return Ok(1);
}
Ok(0)
r.map_err(|err| CodeError::TunnelRpcCallFailed(err).into())
}
pub async fn restart(ctx: CommandContext) -> Result<i32, AnyError> {
do_single_rpc_call(
ctx,
do_single_rpc_call::<_, ()>(
&ctx,
protocol::singleton::METHOD_RESTART,
protocol::EmptyObject {},
)
.await
.map(|_| 0)
}
pub async fn kill(ctx: CommandContext) -> Result<i32, AnyError> {
do_single_rpc_call(
ctx,
do_single_rpc_call::<_, ()>(
&ctx,
protocol::singleton::METHOD_SHUTDOWN,
protocol::EmptyObject {},
)
.await
.map(|_| 0)
}
pub async fn status(ctx: CommandContext) -> Result<i32, AnyError> {
let status: protocol::singleton::Status = do_single_rpc_call(
&ctx,
protocol::singleton::METHOD_STATUS,
protocol::EmptyObject {},
)
.await?;
ctx.log.result(serde_json::to_string(&status).unwrap());
Ok(0)
}
/// Removes unused servers.

View file

@ -164,6 +164,7 @@ pub mod singleton {
pub const METHOD_RESTART: &str = "restart";
pub const METHOD_SHUTDOWN: &str = "shutdown";
pub const METHOD_STATUS: &str = "status";
pub const METHOD_LOG: &str = "log";
#[derive(Serialize)]
@ -179,4 +180,9 @@ pub mod singleton {
pub prefix: String,
pub message: String,
}
#[derive(Serialize, Deserialize)]
pub struct Status {
pub ok: bool,
}
}

View file

@ -83,6 +83,13 @@ pub fn make_singleton_server(
},
);
rpc.register_sync(
protocol::singleton::METHOD_STATUS,
|_: protocol::EmptyObject, _| {
Ok(protocol::singleton::Status { ok: true }) // mostly placeholder
},
);
rpc.register_sync(
protocol::singleton::METHOD_SHUTDOWN,
|_: protocol::EmptyObject, ctx| {

View file

@ -2,9 +2,9 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use crate::constants::{
use crate::{constants::{
APPLICATION_NAME, CONTROL_PORT, DOCUMENTATION_URL, QUALITYLESS_PRODUCT_NAME,
};
}, rpc::ResponseError};
use std::fmt::Display;
use thiserror::Error;
@ -479,16 +479,20 @@ macro_rules! makeAnyError {
/// Note: other error should be migrated to this type gradually
#[derive(Error, Debug)]
pub enum CodeError {
#[error("could not connect to socket/pipe")]
#[error("could not connect to socket/pipe: {0:?}")]
AsyncPipeFailed(std::io::Error),
#[error("could not listen on socket/pipe")]
#[error("could not listen on socket/pipe: {0:?}")]
AsyncPipeListenerFailed(std::io::Error),
#[error("could not create singleton lock file")]
#[error("could not create singleton lock file: {0:?}")]
SingletonLockfileOpenFailed(std::io::Error),
#[error("could not read singleton lock file")]
#[error("could not read singleton lock file: {0:?}")]
SingletonLockfileReadFailed(rmp_serde::decode::Error),
#[error("the process holding the singleton lock file exited")]
#[error("the process holding the singleton lock file (pid={0}) exited")]
SingletonLockedProcessExited(u32),
#[error("no tunnel process is currently running")]
NoRunningTunnel,
#[error("rpc call failed: {0:?}")]
TunnelRpcCallFailed(ResponseError),
}
makeAnyError!(