cli: initial wsl control server

Adds an stdin/out json rpc server for wsl.

Exposes a singular install_local command to install+boot the vscode server on a port from a local archive.

Also refines the common rpc layer some more. I'm decently happy with it now.
This commit is contained in:
Connor Peet
2023-01-17 16:05:25 -08:00
parent ebc7a21d55
commit 2c2ead679b
21 changed files with 554 additions and 137 deletions

View File

@@ -146,6 +146,22 @@ impl<'a> From<&'a CliCore> for CodeServerArgs {
pub enum StandaloneCommands {
/// Updates the CLI.
Update(StandaloneUpdateArgs),
/// Internal commands for WSL serving.
#[clap(hide = true)]
Wsl(WslArgs),
}
#[derive(Args, Debug, Clone)]
pub struct WslArgs {
#[clap(subcommand)]
pub command: WslCommands,
}
#[derive(Subcommand, Debug, Clone)]
pub enum WslCommands {
/// Runs the WSL server on stdin/out
Serve,
}
#[derive(Args, Debug, Clone)]

View File

@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use crate::{
tunnels::{serve_wsl, shutdown_signal::ShutdownSignal},
util::{errors::AnyError, prereqs::PreReqChecker},
};
use super::CommandContext;
pub async fn serve(ctx: CommandContext) -> Result<i32, AnyError> {
let signal = ShutdownSignal::create_rx(&[ShutdownSignal::CtrlC]);
let platform = spanf!(
ctx.log,
ctx.log.span("prereq"),
PreReqChecker::new().verify()
)?;
serve_wsl(
ctx.log,
ctx.paths,
(&ctx.args).into(),
platform,
ctx.http,
signal,
)
.await?;
Ok(0)
}

View File

@@ -5,11 +5,9 @@
use async_trait::async_trait;
use sha2::{Digest, Sha256};
use std::fmt;
use std::str::FromStr;
use sysinfo::{Pid, SystemExt};
use sysinfo::Pid;
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};
use super::{
args::{
@@ -20,6 +18,7 @@ use super::{
};
use crate::tunnels::dev_tunnels::ActiveTunnel;
use crate::tunnels::shutdown_signal::ShutdownSignal;
use crate::{
auth::Auth,
log::{self, Logger},
@@ -93,22 +92,6 @@ impl ServiceContainer for TunnelServiceContainer {
Ok(())
}
}
/// Describes the signal to manully stop the server
pub enum ShutdownSignal {
CtrlC,
ParentProcessKilled,
ServiceStopped,
}
impl fmt::Display for ShutdownSignal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ShutdownSignal::CtrlC => write!(f, "Ctrl-C received"),
ShutdownSignal::ParentProcessKilled => write!(f, "Parent process no longer exists"),
ShutdownSignal::ServiceStopped => write!(f, "Service stopped"),
}
}
}
pub async fn service(
ctx: CommandContext,
@@ -269,32 +252,17 @@ async fn serve_with_csa(
let shutdown_tx = if let Some(tx) = shutdown_rx {
tx
} else {
let (tx, rx) = mpsc::unbounded_channel::<ShutdownSignal>();
if let Some(process_id) = gateway_args.parent_process_id {
match Pid::from_str(&process_id) {
Ok(pid) => {
let tx = tx.clone();
info!(log, "checking for parent process {}", process_id);
tokio::spawn(async move {
let mut s = sysinfo::System::new();
while s.refresh_process(pid) {
sleep(Duration::from_millis(2000)).await;
}
tx.send(ShutdownSignal::ParentProcessKilled).ok();
});
}
Err(_) => {
info!(log, "invalid parent process id: {}", process_id);
}
}
}
tokio::spawn(async move {
tokio::signal::ctrl_c().await.ok();
tx.send(ShutdownSignal::CtrlC).ok();
});
rx
};
} else if let Some(pid) = gateway_args
.parent_process_id
.and_then(|p| Pid::from_str(&p).ok())
{
ShutdownSignal::create_rx(&[
ShutdownSignal::CtrlC,
ShutdownSignal::ParentProcessKilled(pid),
])
} else {
ShutdownSignal::create_rx(&[ShutdownSignal::CtrlC])
};
let mut r = crate::tunnels::serve(&log, tunnel, &paths, &csa, platform, shutdown_tx).await?;
r.tunnel.close().await.ok();