detee-cli/src/bin/detee-cli.rs
2025-06-19 20:43:05 +03:00

626 lines
28 KiB
Rust

// SPDX-License-Identifier: Apache-2.0
use clap::{builder::PossibleValue, Arg, Command};
use detee_cli::general::cli_handler::{
handle_account, handle_completion, handle_operators, handle_packagers,
};
use detee_cli::sgx::cli_handler::{handle_app, handle_app_nodes};
use detee_cli::snp::cli_handler::{handle_vm, handle_vm_nodes};
use detee_cli::*;
const ABOUT: &str = r#"The DeTEE CLI allows you to manage and deploy applications and virtual machines.
All software runs within Trusted Execution Environments on a distributed network.
More information can be found at https://detee.ltd
Feel free to browser applications bundles or VM disks available for immediate deployment."#;
shadow_rs::shadow!(build);
fn main() {
// TODO: figure if there is a more elegant way to solve this than calling default_provider in main
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
let log_level = match std::env::var("LOG_LEVEL") {
Ok(val) => match val.as_str() {
"DEBUG" => log::LevelFilter::Debug,
"INFO" => log::LevelFilter::Info,
_ => log::LevelFilter::Error,
},
_ => log::LevelFilter::Warn,
};
env_logger::builder().filter_level(log_level).format_timestamp(None).init();
let cmd = clap_cmd();
let matches = cmd.clone().get_matches();
match matches.get_one::<String>("format").unwrap().as_str() {
"json" => std::env::set_var("FORMAT", "JSON"),
"yaml" => std::env::set_var("FORMAT", "YAML"),
_ => (),
}
match matches.subcommand() {
Some(("completion", subcom_args)) => handle_completion(subcom_args, cmd),
Some(("app", subcom_args)) => handle_app(subcom_args),
Some(("app-node", subcom_args)) => handle_app_nodes(subcom_args),
Some(("vm", subcom_args)) => handle_vm(subcom_args),
Some(("vm-node", subcom_args)) => handle_vm_nodes(subcom_args),
Some(("operator", subcom_args)) => handle_operators(subcom_args),
Some(("packager", subcom_args)) => cli_print(handle_packagers(subcom_args)),
Some(("account", subcom_args)) => handle_account(subcom_args),
_ => {
println!("No valid subcommand provided. Use --help for more information.");
std::process::exit(0);
}
}
}
fn clap_cmd() -> Command {
Command::new("detee-cli")
.version(build::CLAP_LONG_VERSION)
.author("https://detee.ltd")
.about(ABOUT)
.arg(
Arg::new("format")
.help("format output as JSON or YAML")
.long("format")
.default_value("human")
.value_parser(["human", "json", "yaml"])
)
.subcommand(Command::new("completion")
.about("generates shell completion scripts")
.arg(
Arg::new("shell")
.help("The shell to generate the script for")
.value_parser(["bash", "zsh", "fish"])
.required(true),
)
)
.subcommand(Command::new("app")
.about("a lightweight service that run on Intel SGX")
/*
.subcommand(
Command::new("package")
.about("package new app from x86_64-linux-musl binary")
.arg(
Arg::new("package-type")
.long("package-type")
.help("Enclave package type")
.long_help(
"Type of package, public or private. public packages are signed by DeTEE,".to_string() +
"Public packages are used wellknown mr_enclave for hratls connection into enclave."
)
.default_value("public")
.value_parser(["public", "private"])
)
.arg(
Arg::new("files")
.help("List of binaries to package")
.long_help("List of x86_64 linux musl binaries to package")
.num_args(1..)
.required(true)
.value_name("FILE")
.value_delimiter(' ')
))
*/
.subcommand(
Command::new("deploy")
.about("create new app from a YAML configuration file")
.arg(
Arg::new("yaml-path")
.long("from-yaml")
.help("allows extended config through App deploy config yaml")
.long_help("Allows extended configuration options, including:".to_owned() +
"\n- deploying to a specific node or to a specific city.")
.exclusive(true)
)
.arg(
Arg::new("vcpus")
.long("vcpus")
.default_value("1")
.value_parser(clap::value_parser!(u32).range(1..64))
.help("the number of vCPUs")
)
.arg(
Arg::new("memory")
.long("memory")
.default_value("1500")
.value_parser(clap::value_parser!(u32).range(1000..8000))
.help("memory in MB")
)
.arg(
Arg::new("disk")
.long("disk")
.default_value("2")
.value_parser(clap::value_parser!(u32).range(1..100))
.help("disk size in GB")
)
.arg(
Arg::new("port")
.long("port")
.value_parser(clap::value_parser!(u32).range(0..65535))
.action(clap::ArgAction::Append)
.help("Application exposing port")
.long_help("Port to expose on the application which mapped into the host's public IP's port")
)
.arg(
Arg::new("package")
.long("package")
.help("Enclave package name")
.default_value("base-package")
.value_parser(["base-package", "actix-static-server", "actix-app-info", "go-app-info"])
)
.arg(
Arg::new("name")
.long("name")
.help("app name")
)
.arg(
Arg::new("hours")
.long("hours")
.short('h')
.help("for how many hours should the app run")
.default_value("1")
.value_parser(clap::value_parser!(u64).range(1..5000))
.long_help("How long should the app run for so it locks up LP accordingly")
)
.arg(
Arg::new("price")
.long("price")
.help("price per unit per minute; check docs")
.default_value("200000")
.value_parser(clap::value_parser!(u64).range(1..50000000))
)
.arg(
Arg::new("location")
.help("deploy to a specific location")
.long("location")
.default_value("DE")
.value_parser([
PossibleValue::new("DE").help("Frankfurt am Main, Hesse, Germany"),
]),
)
.arg(
Arg::new("env")
.short('e')
.long("env")
.help("env override")
.long_help("environment variable override on launch config")
.action(clap::ArgAction::Append)
)
.arg(
Arg::new("arg")
.long("arg")
.help("arg override")
.long_help("application arguement variable override on launch config")
.action(clap::ArgAction::Append)
)
)
.subcommand(Command::new("inspect").about("list all available information about a App")
.arg(
Arg::new("uuid")
.help("supply the uuid of the App contract")
.required(true)
)
.arg(
Arg::new("show-node")
.long("show-node")
.help("inspect the node that is runnnig this App")
.action(clap::ArgAction::SetTrue)
)
)
.subcommand(
Command::new("delete")
.about("delete deployed app")
.arg(
Arg::new("uuid")
.help("supply the uuid of the App contract")
.required(true)
)
)
.subcommand(
Command::new("config")
.about("App launch config in a YAML file")
.long_about("The YAML configuration file for the environment variables, child processes, restart policy and arguments for the app")
.subcommand(
Command::new("validate")
.about("Validate a YAML configuration file")
.arg(
Arg::new("config")
.help("App config yaml file path to validate")
.long_help("Validate YAML configuration file for the app which you want to run in the enclave")
.long("from-yaml")
.required(true)
)
)
.subcommand(
Command::new("update")
.about("App launch config yaml file path to update in enclave")
.long_about("Update the YAML configuration file for the app which you want to run in the enclave")
.arg(
Arg::new("config")
.help("Path to yaml file")
.long("from-yaml")
.required(true)
)
.arg(
Arg::new("uuid")
.help("supply the uuid of the App contract")
.required(true)
)
)
.subcommand(
Command::new("get")
.about("download current launch configuration file from enclave")
.arg(
Arg::new("path")
.help("path to save launch config")
.long_help("path to save current launch config from enclave as yaml")
.required(true),
)
.arg(
Arg::new("uuid")
.help("supply the uuid of the App contract")
.required(true)
)
)
)
.subcommand(
Command::new("list")
.about("list all deployed apps")
.long_about("List all the deployed apps in the enclave")
.arg(
Arg::new("as-operator")
.long("as-operator")
.help("list Apps running on your SGX nodes")
.action(clap::ArgAction::SetTrue)
)
)
)
.subcommand(Command::new("app-node")
.about("info about Intel SGX servers registerd to DeTEE")
.subcommand(Command::new("search").about("search nodes based on filters"))
.subcommand(Command::new("inspect").about("get detailed information about a node")
.arg(
Arg::new("ip")
.long("ip")
.help("main IP of the node your want to inspect")
.required(true)
)
)
.subcommand(Command::new("report").about("report a node for poor performance")
.arg(
Arg::new("pubkey")
.long("pubkey")
.help("public key of the node you are reporting")
.required(true)
)
.arg(
Arg::new("contract")
.long("contract")
.help("UUID of the active contract with this node")
.required(true)
)
.arg(
Arg::new("reason")
.long("reason")
.help("detail the performance issue you experienced")
)
)
)
.subcommand(Command::new("vm")
.about("virtual machines that run on AMD SEV-SNP nodes")
.subcommand(Command::new("deploy").about("deploy a VM on the DeTEE network")
.arg(
Arg::new("yaml-path")
.long("from-yaml")
.help("allows extended config through yaml")
.long_help("Allows extended configuration options, including:".to_owned() +
"\n- deploying to a specific node or to a specific city." +
"\n- specifying the kernel and dtrfs version." +
"\n- configuring extra ports to publish for a VM no public IPv4" +
"\n- request a public IPv6 address for yout VM" +
"\nSamples can be found in ~/.detee/samples/new_vm/")
.exclusive(true)
)
.arg(
Arg::new("location")
.help("deploy to a specific location")
.long("location")
.default_value("Vancouver")
.value_parser([
PossibleValue::new("GB").help("London, England, GB"),
PossibleValue::new("Canada").help("Montréal or Vancouver"),
PossibleValue::new("Montreal").help("Montréal, Quebec, CA"),
PossibleValue::new("Vancouver").help("Vancouver, British Columbia, CA"),
PossibleValue::new("California").help("San Jose, California, US"),
PossibleValue::new("US").help("San Jose, California, US"),
PossibleValue::new("France").help("Paris, Île-de-France, FR"),
PossibleValue::new("Random").help("Just deploy somewhere..."),
]),
)
.arg(
Arg::new("vcpus")
.long("vcpus")
.default_value("1")
.value_parser(clap::value_parser!(u32).range(1..64))
.help("the number of vCPUs")
)
.arg(
Arg::new("memory")
.long("memory")
.default_value("1000")
.value_parser(clap::value_parser!(u32).range(800..123000))
.help("memory in MB")
)
.arg(
Arg::new("disk")
.long("disk")
.default_value("10")
.value_parser(clap::value_parser!(u32).range(5..500))
.help("disk size in GB")
)
.arg(
Arg::new("distribution")
.long("distro")
.help("GNU/Linux distribution")
.default_value("arch")
.value_parser(["arch", "ubuntu", "fedora"])
)
.arg(
Arg::new("hours")
.long("hours")
.help("for how many hours should the VM run")
.default_value("1")
.value_parser(clap::value_parser!(u32).range(1..5000))
)
.arg(
Arg::new("price")
.long("price")
.help("price per unit per minute; check docs")
.default_value("20000")
.value_parser(clap::value_parser!(u64).range(1..50000000))
)
.arg(
Arg::new("hostname")
.long("hostname")
.help("hostname of you VM and OS")
)
.arg(
Arg::new("public-ip")
.long("public-ip")
.help("get a public IPv4 address for this VM")
.action(clap::ArgAction::SetTrue)
)
)
.subcommand(Command::new("inspect").about("list all available information about a VM")
.arg(
Arg::new("uuid")
.help("supply the uuid of the VM contract")
.required(true)
)
.arg(
Arg::new("show-node")
.long("show-node")
.help("inspect the node that is runnnig this VM")
.action(clap::ArgAction::SetTrue)
)
)
.subcommand(Command::new("ssh").about("connect to the VM using SSH")
.arg(
Arg::new("uuid")
.help("supply the uuid of the VM contract")
.required(true)
)
.arg(
Arg::new("just-print")
.long("just-print")
.help("print the command instead of connecting")
.action(clap::ArgAction::SetTrue)
)
)
.subcommand(Command::new("list").about("list all existing VM contracts")
.arg(
Arg::new("as-operator")
.long("as-operator")
.help("list VMs running on your SNP nodes")
.action(clap::ArgAction::SetTrue)
)
)
.subcommand(Command::new("delete").about("delete a VM from the DeTEE network")
.arg(
Arg::new("uuid")
.help("uuid of the VM that you wish to delete")
.required(true)
)
)
.subcommand(Command::new("update")
.about("update the hardware or the lifetime of a VM")
.long_about("Allows you to update the hardware or the lifetime".to_string() +
"\nAny hardware modifiations will restart the VM." +
"\nChanging the lifetime of a VM will not restart." +
"\nIf changing the lifetime to a higher value, LP will locked accordingly.")
.arg(
Arg::new("uuid")
.help("supply the uuid of the VM you wish to upgrade")
.required(true)
)
.arg(
Arg::new("hostname")
.long("hostname")
.default_value("")
.help("change the hostname within the smart contract")
)
.arg(
Arg::new("vcpus")
.long("vcpus")
.default_value("0")
.value_parser(clap::value_parser!(u32).range(0..100))
.help("modify the number of vCPUs")
)
.arg(
Arg::new("memory")
.long("memory")
.default_value("0")
.value_parser(clap::value_parser!(u32).range(0..115000))
.help("modify the MB of memory reserved")
)
.arg(
Arg::new("disk")
.long("disk")
.default_value("0")
.value_parser(clap::value_parser!(u32).range(0..500))
.help("increase the size of the disk in GB")
)
.arg(
Arg::new("hours")
.long("hours")
.help("delete vm after the time expires")
.default_value("0")
.value_parser(clap::value_parser!(u32).range(0..5000))
)
.arg(
Arg::new("dtrfs")
.long("dtrfs")
.help(r#""update kernel to "latest" or custom""#)
.long_help("Reboot the VM and upgrade dtrfs together with the kernel.\n".to_owned() +
r#"Specify "latest" to simply upgrade to the latest kernel"# +
"\nAlternatively, you can specify full path to DTRFS config." +
"\nSamples can be found in ~/.detee/samples/dtrfs/")
.default_value("")
)
)
.subcommand(Command::new("distro")
.about("GNU/Linux distributions for Virtual Machines")
.subcommand(
Command::new("search").about("search public registry for distributions"),
)
)
.subcommand(Command::new("dtrfs")
.about("DeTEE initramfs archives required to boot a VM")
.subcommand(Command::new("list").about("list locally stored dtrfs"))
)
)
.subcommand(Command::new("vm-node")
.about("info about AMD SEV-SNP servers registerd to DeTEE")
.subcommand(Command::new("search").about("search nodes based on filters"))
.subcommand(Command::new("inspect").about("get detailed information about a node")
.arg(
Arg::new("ip")
.long("ip")
.help("main IP of the node your want to inspect")
.required(true)
)
)
.subcommand(Command::new("report").about("report a node for poor performance")
.arg(
Arg::new("pubkey")
.long("pubkey")
.help("public key of the node you are reporting")
.required(true)
)
.arg(
Arg::new("contract")
.long("contract")
.help("UUID of the active contract with this node")
.required(true)
)
.arg(
Arg::new("reason")
.long("reason")
.help("detail the performance issue you experienced")
)
)
)
.subcommand(Command::new("operator")
.about("node operators host nodes on the DeTEE network")
.subcommand(Command::new("list").about("list operators currently offering servers"))
.subcommand(Command::new("register").about("register as a node operator to boost rewards")
.arg(
Arg::new("escrow")
.long("escrow")
.help("At least 5000 LP is required as escrow")
.long_help("Escrow is used by node operators to guarantee quality.".to_owned() +
"\nBefore adding escrow, make sure you booted a node under your account." +
"\nWhen all your nodes got decomissioned, your escrow gets automatically returned.")
.default_value("5000")
.value_parser(clap::value_parser!(u64).range(5000..100000))
)
.arg(
Arg::new("email")
.long("email")
.help("email address must be supplied as contact point")
.required(true)
)
)
.subcommand(Command::new("inspect").about("inspect yourself or other opertors")
.arg(
Arg::new("wallet")
.long("wallet")
.help("specify wallet address of the operator")
)
)
.subcommand(Command::new("kick").about("terminate a contract (refund up to 1 week)")
.arg(
Arg::new("contract")
.long("contract")
.help("unique ID of the contract you wish to kick")
)
.arg(
Arg::new("reason")
.long("reason")
.help("optionally mention why you made this decission")
)
)
.subcommand(Command::new("ban-user").about("ban a user from your services")
.arg(
Arg::new("wallet")
.long("wallet")
.required(true)
.help("wallet address of the user you are banning")
)
)
.subcommand(Command::new("decom").about("announce that a node is going offline")
.arg(
Arg::new("pubkey")
.long("pubkey")
.help("public key of the node you are decomissioning")
.required(true)
)
.arg(
Arg::new("days")
.long("days")
.help("days left till the node will go offline")
.value_parser(clap::value_parser!(u64).range(7..365))
)
)
)
.subcommand(Command::new("packager")
.about("show a list of software packagers")
)
.subcommand(Command::new("account")
.about("account and security data for this CLI")
.subcommand(Command::new("show").about("show account data associated with this CLI"),
)
.subcommand(Command::new("sign").about("sign a file using your DeTEE key")
.arg(
Arg::new("path")
.help("supply path to your public SSH key")
.required(true)
)
)
.subcommand(Command::new("ssh-pubkey-path").about("define the SSH pubkey that you want to use")
.arg(
Arg::new("path")
.help("supply path to your public SSH key")
.required(true)
)
)
.subcommand(Command::new("network").about("specify if you connect to testnet or staging")
.arg(
Arg::new("name")
.help("the name of the network that you are connecting to")
.required(true)
)
)
)
}