feat: app deploy --package insted of --package-url

update package handling in CLI and only fetch package config
static package index
fix --from-yaml deployment
improved config handling
This commit is contained in:
Noor 2025-03-25 17:36:15 +05:30
parent 103fc2d7f5
commit 88af26351d
Signed by: noormohammedb
GPG Key ID: D83EFB8B3B967146
4 changed files with 118 additions and 99 deletions

@ -136,28 +136,12 @@ fn clap_cmd() -> Command {
.long_help("Port to expose on the application which mapped into the host's public IP's port")
)
.arg(
Arg::new("package-url")
.long("package-url")
.help("Enclave package url")
.long_help(
"Package registry url, detee-cli app package <binary>".to_string() +
"check detee-cli app package --help for more information"
)
.required(true)
Arg::new("package")
.long("package")
.help("Enclave package name")
.default_value("base-package")
.value_parser(["base-package", "actix-app-info", "go-app-info"])
)
/*
.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("name")
.long("name")

@ -1,16 +1,16 @@
use crate::sgx::utils::deploy_new_app_and_update_config;
use crate::config::Config;
use crate::name_generator::random_app_name;
use crate::sgx::config::{validate_yaml, DeteeCliExt};
use crate::sgx::grpc_brain::{delete_app, list_contracts};
use crate::sgx::grpc_dtpm::{attest_and_send_config, get_config_from_enclave};
use crate::sgx::packaging::package_enclave;
use crate::sgx::utils::{fetch_config_and_mr_enclave, override_envs_and_args_launch_config};
use crate::sgx::utils::{
deploy_new_app_and_update_config, fetch_config, override_envs_and_args_launch_config,
};
use crate::sgx::AppDeleteResponse;
use crate::sgx::{
append_uuid_list, get_app_node, get_app_node_by_contract, get_one_contract, inspect_node,
print_nodes, write_uuid_list,
package_entry_from_name, print_nodes, write_uuid_list,
};
use crate::sgx::{AppContract, AppDeployResponse};
use crate::utils::block_on;
@ -72,25 +72,30 @@ fn handle_package(package_match: &ArgMatches) -> Result<SimpleOutput, Box<dyn st
fn handle_deploy(
deploy_match: &ArgMatches,
) -> Result<AppDeployResponse, Box<dyn std::error::Error>> {
let mut app_deploy_config = if let Some(file_path) = deploy_match.get_one::<String>("yaml-path")
let (mut app_deploy_config, app_launch_config) = if let Some(file_path) =
deploy_match.get_one::<String>("yaml-path")
{
AppDeployConfig::from_path(file_path).unwrap()
// TODO: maybe add launch config on deploy command with --launch-config flag
(AppDeployConfig::from_path(file_path).unwrap(), None)
} else {
let vcpu = *deploy_match.get_one::<u32>("vcpus").unwrap();
let memory_mb = *deploy_match.get_one::<u32>("memory").unwrap();
let disk_mb = *deploy_match.get_one::<u32>("disk").unwrap();
let port =
deploy_match.get_many::<u32>("port").unwrap_or_default().cloned().collect::<Vec<_>>();
let package_url = deploy_match.get_one::<String>("package-url").unwrap().clone();
// let package_type = deploy_match.get_one::<String>("package-type").unwrap().clone();
let package_name = deploy_match.get_one::<String>("package").unwrap().clone();
let hours = *deploy_match.get_one::<u64>("hours").unwrap();
let node_unit_price = *deploy_match.get_one::<u64>("price").unwrap();
let location = deploy_match.get_one::<String>("location").unwrap().as_str();
let app_name =
deploy_match.get_one::<String>("name").cloned().unwrap_or_else(random_app_name);
let envs =
deploy_match.get_many::<String>("env").unwrap_or_default().cloned().collect::<Vec<_>>();
let args =
deploy_match.get_many::<String>("arg").unwrap_or_default().cloned().collect::<Vec<_>>();
// let private_package = package_type == "private";
let private_package = false;
let resource = Resource { vcpu, memory_mb, disk_mb, port };
let node_pubkey = match block_on(get_app_node(resource.clone(), location.into())) {
Ok(node) => node.node_pubkey,
@ -101,41 +106,41 @@ fn handle_deploy(
}
};
AppDeployConfig {
package_url,
resource,
node_unit_price,
hours,
node_pubkey,
private_package,
app_name,
..Default::default()
}
let package_entry = package_entry_from_name(&package_name).unwrap();
let package_url = package_entry.package_url.clone();
let public_package_mr_enclave = Some(package_entry.mr_enclave.to_vec());
let config = block_on(fetch_config(&package_name))?;
let launch_config = override_envs_and_args_launch_config(config, envs, args);
(
AppDeployConfig {
package_url,
resource,
node_unit_price,
hours,
node_pubkey,
private_package,
app_name,
public_package_mr_enclave,
..Default::default()
},
Some(launch_config),
)
};
if app_deploy_config.app_name.is_empty() {
app_deploy_config.app_name = random_app_name();
}
let (mr_enclave, mut launch_config) =
block_on(fetch_config_and_mr_enclave(&app_deploy_config.package_url))?;
app_deploy_config.public_package_mr_enclave = Some(mr_enclave.to_vec());
let envs =
deploy_match.get_many::<String>("env").unwrap_or_default().cloned().collect::<Vec<_>>();
let args =
deploy_match.get_many::<String>("arg").unwrap_or_default().cloned().collect::<Vec<_>>();
override_envs_and_args_launch_config(&mut launch_config, envs, args);
let app_name = app_deploy_config.app_name.clone();
match block_on(deploy_new_app_and_update_config(app_deploy_config, launch_config)) {
match block_on(deploy_new_app_and_update_config(app_deploy_config, app_launch_config)) {
Ok(new_app_res) if new_app_res.error.is_empty() => {
append_uuid_list(&new_app_res.uuid, &app_name)?;
Ok(new_app_res.into())
Ok((new_app_res, app_name).into())
}
Ok(new_app_res) => Err(Box::new(std::io::Error::other(new_app_res.error))),
Err(e) => Err(Box::new(e)),
}

@ -5,6 +5,8 @@ pub mod grpc_dtpm;
pub mod packaging;
pub mod utils;
use std::sync::LazyLock;
use crate::config::Config;
use crate::snp;
use crate::{constants::HRATLS_APP_PORT, utils::block_on};
@ -54,6 +56,47 @@ pub struct AppContract {
pub exposed_host_ports: Vec<(u32, u32)>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PublicIndex {
packages: Vec<PackageElement>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PackageElement {
package_name: String,
package_url: String,
launch_config_url: String,
mr_enclave: [u8; 32],
}
pub static PACKAGES_INDEX: LazyLock<PublicIndex> = LazyLock::new(|| {
PublicIndex { packages: vec![
// TODO: package a general base package
PackageElement{
package_name: "base-package".to_string(),
package_url: "https://registry.detee.ltd/sgx/packages/actix-app-info_package_2025-03-19_13-49-56.tar.gz".to_string(),
launch_config_url: "https://registry.detee.ltd/sgx/launch_configs/actix-app-info-launch-config_001.yaml".to_string(),
mr_enclave: [ 139, 208, 253, 40, 81, 80, 225, 137, 106, 182, 27, 200, 25, 128, 212, 235, 76, 153, 215, 42, 160, 69, 26, 132, 77, 223, 182, 180, 136, 218, 173, 184 ],
},
PackageElement{
package_name: "actix-app-info".to_string(),
package_url: "https://registry.detee.ltd/sgx/packages/actix-app-info_package_2025-03-19_13-49-56.tar.gz".to_string(),
launch_config_url: "https://registry.detee.ltd/sgx/launch_configs/actix-app-info-launch-config_001.yaml".to_string(),
mr_enclave: [ 139, 208, 253, 40, 81, 80, 225, 137, 106, 182, 27, 200, 25, 128, 212, 235, 76, 153, 215, 42, 160, 69, 26, 132, 77, 223, 182, 180, 136, 218, 173, 184 ],
},
// TODO: package a go package
PackageElement{
package_name: "go-app-info".to_string(),
package_url: "https://registry.detee.ltd/sgx/packages/actix-app-info_package_2025-03-19_13-49-56.tar.gz".to_string(),
launch_config_url: "https://registry.detee.ltd/sgx/launch_configs/actix-app-info-launch-config_001.yaml".to_string(),
mr_enclave: [ 139, 208, 253, 40, 81, 80, 225, 137, 106, 182, 27, 200, 25, 128, 212, 235, 76, 153, 215, 42, 160, 69, 26, 132, 77, 223, 182, 180, 136, 218, 173, 184 ],
}
] }
});
pub fn package_entry_from_name(package_name: &str) -> Option<PackageElement> {
PACKAGES_INDEX.packages.iter().find(|package| package.package_name == package_name).cloned()
}
fn display_mins(minutes: &u64) -> String {
let mins = minutes % 60;
let hours = minutes / 60;
@ -142,16 +185,16 @@ pub struct AppDeployResponse {
impl crate::HumanOutput for AppDeployResponse {
fn human_cli_print(&self) {
println!("App deployd with UUID: {}", self.uuid);
println!("App deployd with UUID: {}, App Name: {}", self.uuid, self.name);
}
}
impl From<NewAppRes> for AppDeployResponse {
fn from(value: NewAppRes) -> Self {
impl From<(NewAppRes, String)> for AppDeployResponse {
fn from((value, name): (NewAppRes, String)) -> Self {
Self {
status: value.status,
uuid: value.uuid,
name: "".to_string(),
name,
node_ip: value.ip_address,
hratls_port: value
.mapped_ports

@ -1,12 +1,12 @@
use super::grpc_brain::new_app;
use crate::constants::HRATLS_APP_PORT;
use crate::sgx::get_one_contract;
use crate::sgx::grpc_brain::new_app;
use crate::sgx::grpc_dtpm::attest_and_send_config;
use crate::sgx::package_entry_from_name;
use detee_shared::app_proto::NewAppRes;
use detee_shared::sgx::types::brain::AppDeployConfig;
use detee_shared::sgx::types::dtpm::DtpmConfig;
use detee_shared::sgx::types::dtpm::EnvironmentEntry;
use serde::{Deserialize, Serialize};
use tokio_retry::strategy::FixedInterval;
use tokio_retry::Retry;
@ -50,40 +50,17 @@ pub async fn hratls_url_and_mr_enclave_from_app_id(app_id: &str) -> (String, Opt
(format!("https://{public_ip}:{dtpm_port}"), mr_enclave)
}
#[derive(Serialize, Deserialize, Debug)]
struct PublicIndex {
packages: Vec<PackageElement>,
}
#[derive(Serialize, Deserialize, Debug)]
struct PackageElement {
package_url: String,
launch_config_url: String,
mr_enclave: [u8; 32],
}
pub async fn fetch_config(package_name: &str) -> Result<DtpmConfig, Error> {
let index_package_entry = package_entry_from_name(package_name)
.ok_or(Error::PublicPackage("package not found for ".to_string() + package_name))?;
pub async fn fetch_config_and_mr_enclave(
package_url: &str,
) -> Result<([u8; 32], DtpmConfig), Error> {
let public_packages_index =
reqwest::get("https://registry.detee.ltd/sgx/public_packages_index.yaml")
.await?
.text()
.await?;
let index = serde_yaml::from_str::<PublicIndex>(&public_packages_index)?;
let index_package_entry =
index.packages.iter().find(|package| package.package_url == package_url).ok_or(
Error::PublicPackage("mr_enclave not found for this public package".to_string()),
)?;
let PackageElement { launch_config_url, mr_enclave, .. } = index_package_entry;
let launch_config_url = index_package_entry.launch_config_url.clone();
let launch_config_str = reqwest::get(launch_config_url).await?.text().await?;
let launch_config = serde_yaml::from_str::<DtpmConfig>(&launch_config_str)?;
Ok((*mr_enclave, launch_config))
Ok(launch_config)
}
pub fn calculate_nanolp_for_app(
@ -109,10 +86,13 @@ pub fn calculate_nanolp_for_app(
}
pub fn override_envs_and_args_launch_config(
launch_config: &mut DtpmConfig,
mut launch_config: DtpmConfig,
envs: Vec<String>,
args: Vec<String>,
) {
) -> DtpmConfig {
if envs.is_empty() && args.is_empty() {
return launch_config;
}
for env in envs {
let mut env = env.split("=");
let key = env.next().expect("environment variable must be in the format 'key=value'");
@ -135,22 +115,29 @@ pub fn override_envs_and_args_launch_config(
for arg in args {
launch_config.child_processes.first_mut().unwrap().arguments.push(arg);
}
launch_config
}
pub async fn deploy_new_app_and_update_config(
app_deploy_config: AppDeployConfig,
launch_config: DtpmConfig,
launch_config: Option<DtpmConfig>,
) -> Result<NewAppRes, Error> {
let new_app_res = new_app(app_deploy_config).await?;
if new_app_res.error.is_empty() {
println!("Deploying...");
tokio::time::sleep(tokio::time::Duration::from_millis(3100)).await;
Retry::spawn(FixedInterval::from_millis(500).take(5), || {
log::debug!("retrying attestation and launch config update");
attest_and_send_config(launch_config.clone(), &new_app_res.uuid)
})
.await?;
Ok(new_app_res)
if let Some(launch_config) = launch_config {
println!("Deploying...");
tokio::time::sleep(tokio::time::Duration::from_millis(3100)).await;
Retry::spawn(FixedInterval::from_millis(500).take(5), || {
log::debug!("retrying attestation and launch config update");
attest_and_send_config(launch_config.clone(), &new_app_res.uuid)
})
.await?;
Ok(new_app_res)
} else {
Ok(new_app_res)
}
} else {
Err(Error::Deployment(new_app_res.error))
}