use anyhow::Context;
use wasmer_api::backend::BackendClient;
use wasmer_deploy_schema::schema::{
DeploymentV1, WebcPackageIdentifierV1, WorkloadRunnerV1::WebcCommand,
WorkloadRunnerWebcCommandV1, WorkloadV1,
};
use crate::{config::DeployClientConfig, ApiOpts};
use super::AsyncCliCommand;
#[derive(clap::Parser, Debug)]
pub struct CmdSsh {
#[clap(flatten)]
api: ApiOpts,
}
impl CmdSsh {
async fn exec(self) -> Result<(), anyhow::Error> {
let mut config = crate::config::load_config(None)?;
let client = self.api.client()?;
let (token, is_new) = acquire_ssh_token(&client, &config.config).await?;
if is_new {
eprintln!("Acuired new SSH token");
config.set_ssh_token(token.clone())?;
if let Err(err) = config.save() {
eprintln!("Warning: failed to save config: {err}");
}
}
let exit = std::process::Command::new("ssh")
.args(&["-o", "ControlPath=none"])
.arg(format!("{token}@tokera.sh"))
.arg("sharrattj/dash")
.spawn()?
.wait()?;
if exit.success() {
Ok(())
} else {
Err(anyhow::anyhow!("ssh failed with status {}", exit))
}
}
}
type IsNew = bool;
type RawToken = String;
impl AsyncCliCommand for CmdSsh {
fn run_async(self) -> futures::future::BoxFuture<'static, Result<(), anyhow::Error>> {
Box::pin(self.exec())
}
}
async fn acquire_ssh_token(
client: &BackendClient,
config: &DeployClientConfig,
) -> Result<(RawToken, IsNew), anyhow::Error> {
if let Some(token) = &config.ssh_token {
return Ok((token.clone(), false));
}
let token = create_ssh_token(client).await?;
Ok((token, true))
}
async fn create_ssh_token(client: &BackendClient) -> Result<RawToken, anyhow::Error> {
let name = "ssh-shell";
let depl = DeploymentV1 {
name: name.to_string(),
workload: WorkloadV1 {
name: None,
runner: WebcCommand(WorkloadRunnerWebcCommandV1 {
package: WebcPackageIdentifierV1 {
repository: "https://wapm.dev".parse().unwrap(),
namespace: "sharrattj".to_string(),
name: "dash".to_string(),
tag: None,
},
command: "dash".to_string(),
}),
capabilities: Default::default(),
},
};
let cfg = wasmer_api::backend::publish_deploy_app(
&client,
wasmer_api::backend::gql::PublishDeployAppVariables {
config: serde_json::to_string(&depl)?,
name: name.into(),
owner: None,
},
)
.await
.context("could not publish deployment")?;
wasmer_api::backend::generate_deploy_token_raw(&client, cfg.id.into_inner())
.await
.context("Could not create token for ssh access")
}