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:
parent
103fc2d7f5
commit
88af26351d
@ -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")
|
.long_help("Port to expose on the application which mapped into the host's public IP's port")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("package-url")
|
Arg::new("package")
|
||||||
.long("package-url")
|
.long("package")
|
||||||
.help("Enclave package url")
|
.help("Enclave package name")
|
||||||
.long_help(
|
.default_value("base-package")
|
||||||
"Package registry url, detee-cli app package <binary>".to_string() +
|
.value_parser(["base-package", "actix-app-info", "go-app-info"])
|
||||||
"check detee-cli app package --help for more information"
|
|
||||||
)
|
)
|
||||||
.required(true)
|
|
||||||
)
|
|
||||||
/*
|
|
||||||
.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(
|
||||||
Arg::new("name")
|
Arg::new("name")
|
||||||
.long("name")
|
.long("name")
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use crate::sgx::utils::deploy_new_app_and_update_config;
|
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::name_generator::random_app_name;
|
use crate::name_generator::random_app_name;
|
||||||
use crate::sgx::config::{validate_yaml, DeteeCliExt};
|
use crate::sgx::config::{validate_yaml, DeteeCliExt};
|
||||||
use crate::sgx::grpc_brain::{delete_app, list_contracts};
|
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::grpc_dtpm::{attest_and_send_config, get_config_from_enclave};
|
||||||
use crate::sgx::packaging::package_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::AppDeleteResponse;
|
||||||
use crate::sgx::{
|
use crate::sgx::{
|
||||||
append_uuid_list, get_app_node, get_app_node_by_contract, get_one_contract, inspect_node,
|
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::sgx::{AppContract, AppDeployResponse};
|
||||||
use crate::utils::block_on;
|
use crate::utils::block_on;
|
||||||
@ -72,25 +72,30 @@ fn handle_package(package_match: &ArgMatches) -> Result<SimpleOutput, Box<dyn st
|
|||||||
fn handle_deploy(
|
fn handle_deploy(
|
||||||
deploy_match: &ArgMatches,
|
deploy_match: &ArgMatches,
|
||||||
) -> Result<AppDeployResponse, Box<dyn std::error::Error>> {
|
) -> 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 {
|
} else {
|
||||||
let vcpu = *deploy_match.get_one::<u32>("vcpus").unwrap();
|
let vcpu = *deploy_match.get_one::<u32>("vcpus").unwrap();
|
||||||
let memory_mb = *deploy_match.get_one::<u32>("memory").unwrap();
|
let memory_mb = *deploy_match.get_one::<u32>("memory").unwrap();
|
||||||
let disk_mb = *deploy_match.get_one::<u32>("disk").unwrap();
|
let disk_mb = *deploy_match.get_one::<u32>("disk").unwrap();
|
||||||
let port =
|
let port =
|
||||||
deploy_match.get_many::<u32>("port").unwrap_or_default().cloned().collect::<Vec<_>>();
|
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_name = deploy_match.get_one::<String>("package").unwrap().clone();
|
||||||
// let package_type = deploy_match.get_one::<String>("package-type").unwrap().clone();
|
|
||||||
let hours = *deploy_match.get_one::<u64>("hours").unwrap();
|
let hours = *deploy_match.get_one::<u64>("hours").unwrap();
|
||||||
let node_unit_price = *deploy_match.get_one::<u64>("price").unwrap();
|
let node_unit_price = *deploy_match.get_one::<u64>("price").unwrap();
|
||||||
let location = deploy_match.get_one::<String>("location").unwrap().as_str();
|
let location = deploy_match.get_one::<String>("location").unwrap().as_str();
|
||||||
let app_name =
|
let app_name =
|
||||||
deploy_match.get_one::<String>("name").cloned().unwrap_or_else(random_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 private_package = false;
|
||||||
|
|
||||||
let resource = Resource { vcpu, memory_mb, disk_mb, port };
|
let resource = Resource { vcpu, memory_mb, disk_mb, port };
|
||||||
let node_pubkey = match block_on(get_app_node(resource.clone(), location.into())) {
|
let node_pubkey = match block_on(get_app_node(resource.clone(), location.into())) {
|
||||||
Ok(node) => node.node_pubkey,
|
Ok(node) => node.node_pubkey,
|
||||||
@ -101,6 +106,15 @@ fn handle_deploy(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 {
|
AppDeployConfig {
|
||||||
package_url,
|
package_url,
|
||||||
resource,
|
resource,
|
||||||
@ -109,33 +123,24 @@ fn handle_deploy(
|
|||||||
node_pubkey,
|
node_pubkey,
|
||||||
private_package,
|
private_package,
|
||||||
app_name,
|
app_name,
|
||||||
|
public_package_mr_enclave,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
},
|
||||||
|
Some(launch_config),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if app_deploy_config.app_name.is_empty() {
|
if app_deploy_config.app_name.is_empty() {
|
||||||
app_deploy_config.app_name = random_app_name();
|
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();
|
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() => {
|
Ok(new_app_res) if new_app_res.error.is_empty() => {
|
||||||
append_uuid_list(&new_app_res.uuid, &app_name)?;
|
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))),
|
Ok(new_app_res) => Err(Box::new(std::io::Error::other(new_app_res.error))),
|
||||||
Err(e) => Err(Box::new(e)),
|
Err(e) => Err(Box::new(e)),
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ pub mod grpc_dtpm;
|
|||||||
pub mod packaging;
|
pub mod packaging;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::snp;
|
use crate::snp;
|
||||||
use crate::{constants::HRATLS_APP_PORT, utils::block_on};
|
use crate::{constants::HRATLS_APP_PORT, utils::block_on};
|
||||||
@ -54,6 +56,47 @@ pub struct AppContract {
|
|||||||
pub exposed_host_ports: Vec<(u32, u32)>,
|
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 {
|
fn display_mins(minutes: &u64) -> String {
|
||||||
let mins = minutes % 60;
|
let mins = minutes % 60;
|
||||||
let hours = minutes / 60;
|
let hours = minutes / 60;
|
||||||
@ -142,16 +185,16 @@ pub struct AppDeployResponse {
|
|||||||
|
|
||||||
impl crate::HumanOutput for AppDeployResponse {
|
impl crate::HumanOutput for AppDeployResponse {
|
||||||
fn human_cli_print(&self) {
|
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 {
|
impl From<(NewAppRes, String)> for AppDeployResponse {
|
||||||
fn from(value: NewAppRes) -> Self {
|
fn from((value, name): (NewAppRes, String)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
status: value.status,
|
status: value.status,
|
||||||
uuid: value.uuid,
|
uuid: value.uuid,
|
||||||
name: "".to_string(),
|
name,
|
||||||
node_ip: value.ip_address,
|
node_ip: value.ip_address,
|
||||||
hratls_port: value
|
hratls_port: value
|
||||||
.mapped_ports
|
.mapped_ports
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use super::grpc_brain::new_app;
|
|
||||||
use crate::constants::HRATLS_APP_PORT;
|
use crate::constants::HRATLS_APP_PORT;
|
||||||
use crate::sgx::get_one_contract;
|
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::grpc_dtpm::attest_and_send_config;
|
||||||
|
use crate::sgx::package_entry_from_name;
|
||||||
use detee_shared::app_proto::NewAppRes;
|
use detee_shared::app_proto::NewAppRes;
|
||||||
use detee_shared::sgx::types::brain::AppDeployConfig;
|
use detee_shared::sgx::types::brain::AppDeployConfig;
|
||||||
use detee_shared::sgx::types::dtpm::DtpmConfig;
|
use detee_shared::sgx::types::dtpm::DtpmConfig;
|
||||||
use detee_shared::sgx::types::dtpm::EnvironmentEntry;
|
use detee_shared::sgx::types::dtpm::EnvironmentEntry;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tokio_retry::strategy::FixedInterval;
|
use tokio_retry::strategy::FixedInterval;
|
||||||
use tokio_retry::Retry;
|
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)
|
(format!("https://{public_ip}:{dtpm_port}"), mr_enclave)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
pub async fn fetch_config(package_name: &str) -> Result<DtpmConfig, Error> {
|
||||||
struct PublicIndex {
|
let index_package_entry = package_entry_from_name(package_name)
|
||||||
packages: Vec<PackageElement>,
|
.ok_or(Error::PublicPackage("package not found for ".to_string() + package_name))?;
|
||||||
}
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct PackageElement {
|
|
||||||
package_url: String,
|
|
||||||
launch_config_url: String,
|
|
||||||
mr_enclave: [u8; 32],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn fetch_config_and_mr_enclave(
|
let launch_config_url = index_package_entry.launch_config_url.clone();
|
||||||
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_str = reqwest::get(launch_config_url).await?.text().await?;
|
let launch_config_str = reqwest::get(launch_config_url).await?.text().await?;
|
||||||
|
|
||||||
let launch_config = serde_yaml::from_str::<DtpmConfig>(&launch_config_str)?;
|
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(
|
pub fn calculate_nanolp_for_app(
|
||||||
@ -109,10 +86,13 @@ pub fn calculate_nanolp_for_app(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn override_envs_and_args_launch_config(
|
pub fn override_envs_and_args_launch_config(
|
||||||
launch_config: &mut DtpmConfig,
|
mut launch_config: DtpmConfig,
|
||||||
envs: Vec<String>,
|
envs: Vec<String>,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
) {
|
) -> DtpmConfig {
|
||||||
|
if envs.is_empty() && args.is_empty() {
|
||||||
|
return launch_config;
|
||||||
|
}
|
||||||
for env in envs {
|
for env in envs {
|
||||||
let mut env = env.split("=");
|
let mut env = env.split("=");
|
||||||
let key = env.next().expect("environment variable must be in the format 'key=value'");
|
let key = env.next().expect("environment variable must be in the format 'key=value'");
|
||||||
@ -135,14 +115,18 @@ pub fn override_envs_and_args_launch_config(
|
|||||||
for arg in args {
|
for arg in args {
|
||||||
launch_config.child_processes.first_mut().unwrap().arguments.push(arg);
|
launch_config.child_processes.first_mut().unwrap().arguments.push(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
launch_config
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn deploy_new_app_and_update_config(
|
pub async fn deploy_new_app_and_update_config(
|
||||||
app_deploy_config: AppDeployConfig,
|
app_deploy_config: AppDeployConfig,
|
||||||
launch_config: DtpmConfig,
|
launch_config: Option<DtpmConfig>,
|
||||||
) -> Result<NewAppRes, Error> {
|
) -> Result<NewAppRes, Error> {
|
||||||
let new_app_res = new_app(app_deploy_config).await?;
|
let new_app_res = new_app(app_deploy_config).await?;
|
||||||
|
|
||||||
if new_app_res.error.is_empty() {
|
if new_app_res.error.is_empty() {
|
||||||
|
if let Some(launch_config) = launch_config {
|
||||||
println!("Deploying...");
|
println!("Deploying...");
|
||||||
tokio::time::sleep(tokio::time::Duration::from_millis(3100)).await;
|
tokio::time::sleep(tokio::time::Duration::from_millis(3100)).await;
|
||||||
Retry::spawn(FixedInterval::from_millis(500).take(5), || {
|
Retry::spawn(FixedInterval::from_millis(500).take(5), || {
|
||||||
@ -151,6 +135,9 @@ pub async fn deploy_new_app_and_update_config(
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
Ok(new_app_res)
|
Ok(new_app_res)
|
||||||
|
} else {
|
||||||
|
Ok(new_app_res)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Deployment(new_app_res.error))
|
Err(Error::Deployment(new_app_res.error))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user