From 5c341af5f614fcfc3f97b837cf005f4fd4ba625f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E5=85=89=E5=B0=91=E5=B9=B4?= <849317537@qq.com> Date: Fri, 9 Aug 2024 13:39:31 +0000 Subject: [PATCH] =?UTF-8?q?restart=20=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.rs | 90 +++++++++++++++++++++++++++++++++--------- src/network.rs | 104 +++++++++++++++++++++++-------------------------- 2 files changed, 120 insertions(+), 74 deletions(-) diff --git a/src/main.rs b/src/main.rs index 866db4f..e239102 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,7 +65,11 @@ struct RockerArgs { // stop "container_id_1, container_id_2, container_id_3" #[arg(long)] - stop: Option + stop: Option, + + // start container_id_1 + #[arg(long)] + restart: Option, } @@ -354,7 +358,6 @@ fn start(is_wait: bool, cb: CloneCb, clong_flags: CloneFlags) -> Result{ Err(RockerError::OtherError(format!("clone err: {e}"))) } } - } fn run_container(_container_id: &String, cmd: &String, args: &RockerArgs, volume_path: &PathBuf, is_exec: bool) -> Result { @@ -435,8 +438,8 @@ impl Display for ContainerStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::READY => write!(f, "😀"), - Self::RUNNING => write!(f, "✅"), - Self::STOP => write!(f, "❌"), + Self::RUNNING => write!(f, "🟢"), + Self::STOP => write!(f, "🔴"), } } @@ -452,6 +455,7 @@ struct ContainerInfo { volume: String, // /root/tmp:/root/tmp,/root/tmp1:/root/tmp1 env: String, // a=1,b=2,c=3 或者 env文件路径 status: ContainerStatus, + log: bool } impl Display for ContainerInfo { @@ -472,6 +476,7 @@ fn save_container_info(args: &RockerArgs, container_id: &String, pid: i32) -> Re volume: args.volume.clone().unwrap_or("".to_string()), env: args.env.clone().unwrap_or("".to_string()), status: ContainerStatus::READY, + log: args.log, }; let toml_str = toml::to_string(&container_info)?; fs::write(container_info_path, toml_str)?; @@ -534,9 +539,9 @@ fn stop_container(containers_id: &str, is_remove: bool) -> Result<()> { return Ok(()) } for container_id in containers_id.split(" ") { + let container_work_path = Path::new(WORKSPACE).join("containers").join(container_id); + let container_merged_path = container_work_path.join("merged"); if let Ok(container_info) = get_container_info(container_id) { - let container_work_path = Path::new(WORKSPACE).join("containers").join(container_id); - let container_merged_path = container_work_path.join("merged"); println!("container_merged_path: {container_merged_path:?}"); // 正在运行中的需要 kill if container_info.status == ContainerStatus::RUNNING { @@ -580,31 +585,79 @@ fn stop_container(containers_id: &str, is_remove: bool) -> Result<()> { } } } else { - println!("容器不存在: {container_id}") + println!("容器不存在: {container_id}, 尝试删除"); + // todo 需要强制删除一下目录 + match fs::remove_dir_all(container_work_path) { + Ok(_) => println!("删除容器 {container_id} 成功"), + Err(e) => println!("删除容器失败: {e:?}"), + } } } - Ok(()) } fn main() -> Result<()>{ - let args = RockerArgs::parse(); - if let (Some(cmd), Some(image_name)) = (&args.run, &args.image) { - // run - let volume_path = extend_image(image_name)?; - let container_id = uuid::Uuid::new_v4().to_string()[0..8].to_string(); - let mut pid = -1; - match run_container(&container_id,&cmd, &args, &volume_path, false) { + let mut args = RockerArgs::parse(); + if args.image.is_some() || args.restart.is_some() { + let volume_path; + let container_id; + let cmd; + match (&args.run, &args.image, &args.restart) { + (Some(_cmd), Some(image_name), None) => { + volume_path = extend_image(image_name)?; + container_id = uuid::Uuid::new_v4().to_string()[0..8].to_string(); + cmd = _cmd.clone(); + } + (None, None, Some(_container_id)) => { + let container_info = get_container_info(_container_id)?; + volume_path = extend_image(&container_info.image)?; + container_id = _container_id.clone(); + cmd = container_info.run; + + args.run = Some(cmd.clone()); + args.image = Some(container_info.image); + args.log = container_info.log; + if container_info.volume.len() > 0 { + args.volume = Some(container_info.volume); + } + if container_info.env.len() > 0 { + args.env = Some(container_info.env); + } + stop_container(&container_id, false)?; + } + _ => { + unreachable!() + } + } + match run_container(&container_id, &cmd, &args, &volume_path, false) { Ok(child_pid) => { - pid = child_pid; + save_container_info(&args, &container_id, child_pid)?; } Err(e) => { + // clone 之后的错误 这里已经无法捕获了 println!("run_container失败: {e}"); + let is_remove = args.restart.is_none(); + stop_container(&container_id, is_remove)?; } } - save_container_info(&args, &container_id, pid)?; // todo 无论出不错, 都要保存一个信息, 后面需要删除用清理 - } else if args.ps || args.psa { + } + // if let (Some(cmd), Some(image_name)) = (&args.run, &args.image) { + // // run + // let volume_path = extend_image(image_name)?; + // let container_id = uuid::Uuid::new_v4().to_string()[0..8].to_string(); + // match run_container(&container_id, &cmd, &args, &volume_path, false) { + // Ok(child_pid) => { + // save_container_info(&args, &container_id, child_pid)?; + // } + // Err(e) => { + // // clone 之后的错误 这里已经无法捕获了 + // println!("run_container失败: {e}"); + // stop_container(&container_id, true)?; + // } + // } + // } + else if args.ps || args.psa { // --ps show_containers(args.psa)? } else if let Some(containers_id) = &args.rm { @@ -614,6 +667,7 @@ fn main() -> Result<()>{ // --stop stop_container(containers_id, false)?; } else if let (Some(cmd), Some(container_id)) = (&args.run, &args.exec) { + // --exec run_container(container_id, &cmd, &args, &Default::default(), true).unwrap(); } diff --git a/src/network.rs b/src/network.rs index cb43a81..35e4983 100644 --- a/src/network.rs +++ b/src/network.rs @@ -1,6 +1,8 @@ -use std::{fs::OpenOptions, io::Read, os::unix::net}; +use std::{fs::OpenOptions, io::Read}; use rand::Rng; +static NETWORK_FILE: &str = "/home/rocker/network"; + fn get_gateway_addr(addr: &str) -> String { let prefix = &addr[..addr.len() - 1]; format!("{}0", prefix) @@ -8,75 +10,75 @@ fn get_gateway_addr(addr: &str) -> String { fn create_bridge_dev(bridge_name: &str) { // brctl addbr br0 let args = ["addbr", bridge_name]; - println!("create_bridge_dev: {args:?}"); let out = std::process::Command::new("brctl") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("create_bridge_dev: {args:?}"); + println!("{:?}", out); } } fn set_bridge_ip(bridge_name: &str, addr: &str){ // 设置容器内从ip: ip addr add 192.168.124.1/24 dev bridge_name let args = ["addr", "add", &format!("{addr}/24"), "dev", bridge_name]; - println!("set_bridge_ip: {args:?}"); let out = std::process::Command::new("ip") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("set_bridge_ip: {args:?}"); + println!("{:?}", out); } } fn set_slave_ip(slave_veth_name: &str, addr: &str, pid: &str) { // nsenter -t 10050 -n -- ip addr add / dev veth1 let args = ["-t", pid, "-n", "--", "ip", "addr", "add", &format!("{addr}/24"), "dev", slave_veth_name]; - println!("set_slave_ip: {args:?}"); let out = std::process::Command::new("nsenter") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("set_slave_ip: {args:?}"); + println!("{:?}", out); } } fn set_snat(gateway_addr: &str, bridge_name: &str) { // sudo iptables -t nat -A POSTROUTING -s 172.18.0.0/24 ! -o br0 -j MASQUERADE let args = ["-t", "nat", "-A", "POSTROUTING", "-s", &format!("{gateway_addr}/24"), "!", "-o", bridge_name, "-j", "MASQUERADE"]; - println!("set_snat: {args:?}"); let out = std::process::Command::new("iptables") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("set_snat: {args:?}"); + println!("{:?}", out); } } fn set_iptables_forward() { // sudo sysctl net.ipv4.conf.all.forwarding=1 let args = ["net.ipv4.conf.all.forwarding=1"]; - println!("set_iptables_forward: {args:?}"); let out = std::process::Command::new("sysctl") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("set_iptables_forward: {args:?}"); + println!("{:?}", out); } // sudo iptables -t filter -P FORWARD ACCEPT let args = ["-t", "filter", "-P", "FORWARD", "ACCEPT"]; - println!("set_iptables_forward: {args:?}"); let out = std::process::Command::new("iptables") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("set_iptables_forward: {args:?}"); + println!("{:?}", out); } } @@ -84,58 +86,58 @@ fn set_iptables_forward() { fn set_slave_route(slave_veth_name: &str, bridge_addr: &str, pid: &str) { // sudo nsenter -t 1851 -n -- ip route add default via 172.18.0.1 dev veth3 let args = ["-t", pid, "-n", "--", "ip", "route", "add", "default", "via", bridge_addr, "dev", slave_veth_name]; - println!("set_slave_route: {args:?}"); let out = std::process::Command::new("nsenter") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("set_slave_route: {args:?}"); + println!("{:?}", out); } } fn set_net_up(bridge_name: &str, master_veth_name: &str, pid: &str, slave_veth_name: &str) { // sudo ip link set bridge_name up let args = ["link", "set", bridge_name, "up"]; - println!("set_up: {args:?}"); let out = std::process::Command::new("ip") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("set_up: {args:?}"); + println!("{:?}", out); } // sudo ip link set master_veth_name up let args = ["link", "set", master_veth_name, "up"]; - println!("set_up: {args:?}"); let out = std::process::Command::new("ip") .args(args) .output() .unwrap(); if !out.status.success() { + println!("set_up: {args:?}"); println!("{:?}", out); } // nsenter -t 1970 -n -- ip link set dev slave_veth_name up let args = ["-t", pid, "-n", "--", "ip", "link", "set", "dev", slave_veth_name, "up"]; - println!("set_up: {args:?}"); let out = std::process::Command::new("nsenter") .args(args) .output() .unwrap(); if !out.status.success() { + println!("set_up: {args:?}"); println!("{:?}", out); } // ip link set lo up let args = ["-t", pid, "-n", "--", "ip", "link", "set", "lo", "up"]; - println!("set_up: {args:?}"); let out = std::process::Command::new("nsenter") .args(args) .output() .unwrap(); if !out.status.success() { + println!("set_up: {args:?}"); println!("{:?}", out); } } @@ -211,20 +213,19 @@ fn del_bridge(bridge_name: &str) { fn create_veth_pair(master_veth_name: &str, slave_veth_name: &str) { // sudo ip link add master_veth_name type veth peer name slave_veth_name - let args = ["link", "add", master_veth_name, "type", "veth", "peer", "name", slave_veth_name]; - println!("create_veth_pair: {args:?}"); let out = std::process::Command::new("ip") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("create_veth_pair: {args:?}"); + println!("{:?}", out); } } -#[derive(Debug)] +#[derive(Debug, Clone)] struct NetWrok { gateway_addr: String, // 192.168.124.0 bridge_addr: String, // 192.168.124.1 @@ -236,12 +237,12 @@ struct NetWrok { } fn get_all_network() -> Vec { - // 打开 /home/rocker/network 所有已经配置的信息, 如果没有则创建 + // 打开 NETWORK_FILE 所有已经配置的信息, 如果没有则创建 let mut f = OpenOptions::new() .write(true) .create(true) .read(true) - .open("/home/rocker/network") + .open(NETWORK_FILE) .unwrap(); let mut text = String::new(); f.read_to_string(&mut text).unwrap(); @@ -252,13 +253,13 @@ fn master_join_bridge(bridge_name: &str, master_veth_name: &str) { // 将主端链接到桥 //sudo brctl addif bridge_name master_veth_name let args = ["addif", bridge_name, master_veth_name]; - println!("master_join_bridge: {args:?}"); let out = std::process::Command::new("brctl") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("master_join_bridge: {args:?}"); + println!("{:?}", out); } } @@ -266,29 +267,28 @@ fn slave_join_bridge(slave_veth_name: &str, pid: &str) { // 将从端 链接到容器 // sudo ip link set slave_veth_name netns 1234 let args = ["link", "set", slave_veth_name, "netns", pid]; - println!("slave_join_bridge: {args:?}"); - let out = std::process::Command::new("ip") .args(args) .output() .unwrap(); if !out.status.success() { - panic!("{:?}", out); + println!("slave_join_bridge: {args:?}"); + println!("{:?}", out); } } fn create_network(uuid_name: &str, pid: i32) { // 创建一个随机地址段的 没分配过的ip - let all_network = get_all_network(); + let mut all_network = get_all_network(); let mut rg = rand::thread_rng(); let network = loop { // 生成一个随机桥ip - let base_addr = format!("{}.{}.{}", rg.gen_range(150..190), rg.gen_range(150..190), rg.gen_range(150..190)); + let base_addr = format!("10.{}.{}", rg.gen_range(0..255), rg.gen_range(0..255)); let gateway_addr = format!("{base_addr}.0"); let bridge_addr = format!("{base_addr}.1"); let slave_addr = format!("{base_addr}.3"); - + if all_network.iter().any(|n|n.bridge_addr == bridge_addr) == false { let bridge_name = format!("ro_{uuid_name}_1"); let master_veth_name = format!("ro_{uuid_name}_2"); @@ -305,6 +305,8 @@ fn create_network(uuid_name: &str, pid: i32) { } }; + all_network.push(network.clone()); + // 写入到文件中 let line = format!("{},{},{},{},{},{}", network.gateway_addr, network.bridge_addr, network.slave_addr, network.bridge_name, network.master_veth_name, network.slave_veth_name); println!("{line:?}"); @@ -330,32 +332,22 @@ fn create_network(uuid_name: &str, pid: i32) { // 设置snat set_snat(&network.gateway_addr, &network.bridge_name); - - // // 创建veth pair - // create_veth_pair(&network.master_veth_name, &network.slave_veth_name); - // // 把从 pair 添加到容器内 - // slave_join_bridge(&network.slave_veth_name, pid.to_string().as_str()); - // // 系统中创建桥 - // create_bridge_dev(&network.bridge_name); - // // 宿主机 主pair 连接桥 - // master_join_bridge(&network.bridge_name, &network.master_veth_name); - // // 给桥分配ip - // set_bridge_ip(&network.bridge_name, &network.bridge_addr); - // // 给容器内 pair 分配ip - // set_slave_ip(&network.slave_veth_name, &network.slave_addr, pid.to_string().as_str()); - // // 激活 上线 - // set_net_up(&network.bridge_name, &network.master_veth_name, pid.to_string().as_str(), &&network.slave_veth_name); - // // 配置路由 + // 如果出错, 清理资源 +} +fn remove_network(uuid_name: &str,) { + let bridge_name = format!("ro_{uuid_name}_1"); + let master_veth_name = format!("ro_{uuid_name}_2"); + let slave_veth_name = format!("ro_{uuid_name}_3"); - // let mut f = OpenOptions::new() - // .write(true) - // .create(true) - // .read(true) - // .open("/home/rocker/network") - // .unwrap(); + // + let mut all_network = get_all_network(); + // 删除 bridge_name 对应的 NetWork + all_network.retain(|n|n.bridge_name != bridge_name); + // todo 回写到文件中 + } fn main(){