From dfe5162f6615eeb095a8ff225e8674286ba69bc8 Mon Sep 17 00:00:00 2001 From: "Rodrigo Rodriguez (Pragmatismo)" Date: Sun, 15 Mar 2026 20:49:08 -0300 Subject: [PATCH] feat(install): add --container-only flag to install command - Add --container-only flag to create container without completing full installation - Exit immediately after container creation - Useful for manual setup or debugging installation issues --- src/core/package_manager/cli.rs | 17 +++++-- src/core/package_manager/facade.rs | 75 ++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/core/package_manager/cli.rs b/src/core/package_manager/cli.rs index af7572d0..a0857f95 100644 --- a/src/core/package_manager/cli.rs +++ b/src/core/package_manager/cli.rs @@ -84,7 +84,7 @@ pub async fn run() -> Result<()> { } "install" => { if args.len() < 3 { - eprintln!("Usage: botserver install [--container] [--tenant ]"); + eprintln!("Usage: botserver install [--container] [--container-only] [--tenant ]"); return Ok(()); } let component = &args[2]; @@ -99,17 +99,26 @@ pub async fn run() -> Result<()> { } else { InstallMode::Local }; + let container_only = args.contains(&"--container-only".to_string()); let tenant = if let Some(idx) = args.iter().position(|a| a == "--tenant") { args.get(idx + 1).cloned() } else { None }; - let pm = PackageManager::new(mode, tenant)?; - let result = pm.install(component).await?; - println!("* Component '{}' installed successfully", component); + let pm = PackageManager::new(mode.clone(), tenant)?; + + let result = if container_only && mode == InstallMode::Container { + Some(pm.install_container_only(component)?) + } else { + pm.install(component).await? + }; if let Some(install_result) = result { install_result.print(); + if container_only { + println!("\n* Container created successfully (--container-only mode)"); + println!("* Run without --container-only to complete installation"); + } } } "remove" => { diff --git a/src/core/package_manager/facade.rs b/src/core/package_manager/facade.rs index 357561b1..49b95891 100644 --- a/src/core/package_manager/facade.rs +++ b/src/core/package_manager/facade.rs @@ -163,6 +163,72 @@ impl PackageManager { self.run_commands(post_cmds, "local", &component.name)?; Ok(()) } + pub fn install_container_only(&self, component_name: &str) -> Result { + let container_name = format!("{}-{}", self.tenant, component_name); + + let _ = safe_lxd(&["init", "--auto"]); + + let images = [ + "ubuntu:24.04", + "ubuntu:22.04", + "images:debian/12", + "images:debian/11", + ]; + + let mut last_error = String::new(); + let mut success = false; + + for image in &images { + info!("Attempting to create container with image: {}", image); + let output = safe_lxc(&[ + "launch", + image, + &container_name, + "-c", + "security.privileged=true", + ]); + + let output = match output { + Some(o) => o, + None => continue, + }; + + if output.status.success() { + info!("Successfully created container with image: {}", image); + success = true; + break; + } + last_error = String::from_utf8_lossy(&output.stderr).to_string(); + warn!("Failed to create container with {}: {}", image, last_error); + + let _ = safe_lxc(&["delete", &container_name, "--force"]); + } + + if !success { + return Err(anyhow::anyhow!( + "LXC container creation failed with all images. Last error: {}", + last_error + )); + } + + std::thread::sleep(std::time::Duration::from_secs(15)); + + let container_ip = Self::get_container_ip(&container_name)?; + + info!("Container '{}' created successfully at IP: {}", container_name, container_ip); + + Ok(InstallResult { + component: component_name.to_string(), + container_name: container_name.clone(), + container_ip: container_ip.clone(), + ports: vec![], + env_vars: std::collections::HashMap::new(), + connection_info: format!( + "Container '{}' created successfully at IP: {}\nRun without --container-only to complete installation.", + container_name, container_ip + ), + }) + } pub fn install_container(&self, component: &ComponentConfig) -> Result { let container_name = format!("{}-{}", self.tenant, component.name); @@ -216,6 +282,15 @@ impl PackageManager { "mkdir -p /opt/gbo/bin /opt/gbo/data /opt/gbo/conf /opt/gbo/logs", )?; + self.exec_in_container( + &container_name, + "echo 'nameserver 8.8.8.8' > /etc/resolv.conf", + )?; + self.exec_in_container( + &container_name, + "echo 'nameserver 8.8.4.4' >> /etc/resolv.conf", + )?; + self.exec_in_container(&container_name, "apt-get update -qq")?; self.exec_in_container( &container_name,