From 72dd36b4cefee5a85145103771d9352585cc9388 Mon Sep 17 00:00:00 2001
From: Yuhang Wei <weiyuhang3@huawei.com>
Date: Fri, 13 Dec 2024 15:31:09 +0800
Subject: [PATCH 1/2] fix(kbimg): add info log for successful image creation

Signed-off-by: Yuhang Wei <weiyuhang3@huawei.com>
---
 KubeOS-Rust/kbimg/src/main.rs | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/KubeOS-Rust/kbimg/src/main.rs b/KubeOS-Rust/kbimg/src/main.rs
index 43e9d422..0af7ff89 100644
--- a/KubeOS-Rust/kbimg/src/main.rs
+++ b/KubeOS-Rust/kbimg/src/main.rs
@@ -15,7 +15,7 @@ use std::{fs, path::PathBuf, process::exit};
 use anyhow::Result;
 use clap::Parser;
 use env_logger::{Builder, Env, Target};
-use log::{debug, error};
+use log::{debug, error, info};
 
 mod admin_container;
 mod commands;
@@ -55,6 +55,7 @@ fn process(info: Box<dyn CreateImage>, mut config: Config, debug: bool) -> Resul
     let path = info.generate_scripts(&config)?;
     if !debug {
         execute_scripts(path)?;
+        info!("Image created successfully");
     } else {
         debug!("Executed following command to generate KubeOS image: bash {:?}", path);
     }
@@ -129,14 +130,9 @@ fn main() {
     }
 
     if let Some(i) = info {
-        match process(i, data, cli.debug) {
-            Ok(_) => {
-                println!("Image created successfully");
-            },
-            Err(e) => {
-                error!("Failed to create image: {:?}", e);
-                exit(1);
-            },
+        if let Err(e) = process(i, data, cli.debug) {
+            error!("Failed to create image: {:?}", e);
+            exit(1);
         }
     }
     exit(0);
-- 
Gitee


From 9030b7f223e1fec1ab4c3612dbcc4b231743ad25 Mon Sep 17 00:00:00 2001
From: Yuhang Wei <weiyuhang3@huawei.com>
Date: Fri, 13 Dec 2024 15:31:22 +0800
Subject: [PATCH 2/2] fix(kbimg): enforce non-empty strings for required fields
 in deserialization

Signed-off-by: Yuhang Wei <weiyuhang3@huawei.com>
---
 KubeOS-Rust/kbimg/src/commands.rs | 46 ++++++++++++++++++++++++++++++-
 KubeOS-Rust/kbimg/src/main.rs     |  8 +++++-
 KubeOS-Rust/kbimg/src/repo.rs     |  4 +--
 3 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/KubeOS-Rust/kbimg/src/commands.rs b/KubeOS-Rust/kbimg/src/commands.rs
index c5bc3890..24fc1031 100644
--- a/KubeOS-Rust/kbimg/src/commands.rs
+++ b/KubeOS-Rust/kbimg/src/commands.rs
@@ -54,14 +54,17 @@ pub enum CreateType {
 #[derive(Debug, Deserialize, Clone)]
 pub struct RepoInfo {
     /// Required: KubeOS version
+    #[serde(deserialize_with = "reject_empty_string")]
     pub version: String,
     /// Required: Repo path for installing packages
     pub repo_path: PathBuf,
     /// Required: Path to the os-agent binary
     pub agent_path: PathBuf,
     /// Required: Encrypted password for root user
+    #[serde(deserialize_with = "reject_empty_string")]
     pub root_passwd: String,
     /// Required for creating upgrade docker image
+    #[serde(default, deserialize_with = "reject_empty_option_string")]
     pub upgrade_img: Option<String>,
     /// Required: RPM packages
     pub rpmlist: Vec<String>,
@@ -84,6 +87,7 @@ pub struct DockerImgInfo {
 #[derive(Debug, Deserialize, Clone)]
 pub struct AdminContainerInfo {
     /// Required: Name of the container image
+    #[serde(deserialize_with = "reject_empty_string")]
     pub img_name: String,
     /// Required: Path to the hostshell binary
     pub hostshell: PathBuf,
@@ -107,21 +111,28 @@ pub struct Config {
 
 #[derive(Deserialize, Debug, Clone)]
 pub struct User {
+    #[serde(deserialize_with = "reject_empty_string")]
     pub name: String,
+    #[serde(deserialize_with = "reject_empty_string")]
     pub passwd: String,
+    #[serde(default, deserialize_with = "reject_empty_option_string")]
     pub primary_group: Option<String>,
     pub groups: Option<Vec<String>>,
 }
 
 #[derive(Deserialize, Debug, Clone)]
 pub struct CopyFile {
+    #[serde(deserialize_with = "reject_empty_string")]
     pub src: String,
+    #[serde(deserialize_with = "reject_empty_string")]
     pub dst: String,
+    #[serde(default, deserialize_with = "reject_empty_option_string")]
     pub create_dir: Option<String>,
 }
 
 #[derive(Deserialize, Debug, Clone)]
 pub struct Grub {
+    #[serde(deserialize_with = "reject_empty_string")]
     pub passwd: String,
 }
 
@@ -149,19 +160,28 @@ pub struct PersistMkdir {
 
 #[derive(Debug, Deserialize, Clone)]
 pub struct PxeConfig {
+    #[serde(deserialize_with = "reject_empty_string")]
     pub rootfs_name: String,
+    #[serde(deserialize_with = "reject_empty_string")]
     pub disk: String,
+    #[serde(deserialize_with = "reject_empty_string")]
     pub server_ip: String,
-    pub local_ip: Option<String>,
+    #[serde(deserialize_with = "reject_empty_string")]
     pub route_ip: String,
+    #[serde(default, deserialize_with = "reject_empty_option_string")]
+    pub local_ip: Option<String>,
+    #[serde(default, deserialize_with = "reject_empty_option_string")]
     pub netmask: Option<String>,
+    #[serde(default, deserialize_with = "reject_empty_option_string")]
     pub net_name: Option<String>,
     pub dhcp: Option<bool>,
 }
 
 #[derive(Debug, Deserialize, Clone)]
 pub struct DmVerity {
+    #[serde(deserialize_with = "reject_empty_string")]
     pub efi_key: String,
+    #[serde(deserialize_with = "reject_empty_string")]
     pub grub_key: String,
     pub keys_dir: Option<PathBuf>,
 }
@@ -196,3 +216,27 @@ impl From<&str> for ImageType {
         }
     }
 }
+
+fn reject_empty_option_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
+where
+    D: serde::Deserializer<'de>,
+{
+    let opt = Option::<String>::deserialize(deserializer)?;
+    if let Some(ref value) = opt {
+        if value.trim().is_empty() {
+            return Err(serde::de::Error::custom("String in Option should not be an empty string if provided"));
+        }
+    }
+    Ok(opt)
+}
+
+fn reject_empty_string<'de, D>(deserializer: D) -> Result<String, D::Error>
+where
+    D: serde::Deserializer<'de>,
+{
+    let value: String = Deserialize::deserialize(deserializer)?;
+    if value.trim().is_empty() {
+        return Err(serde::de::Error::custom("String field should not be empty"));
+    }
+    Ok(value)
+}
diff --git a/KubeOS-Rust/kbimg/src/main.rs b/KubeOS-Rust/kbimg/src/main.rs
index 0af7ff89..878d41f4 100644
--- a/KubeOS-Rust/kbimg/src/main.rs
+++ b/KubeOS-Rust/kbimg/src/main.rs
@@ -74,7 +74,13 @@ fn main() {
     };
     debug!("Config file path: {:?}", config);
     let content = fs::read_to_string(config).expect("Failed to read config file");
-    let data: Config = toml::from_str(&content).expect("Failed to parse toml file");
+    let data: Config = match toml::from_str(&content) {
+        Ok(d) => d,
+        Err(e) => {
+            error!("Failed to parse config file: {}", e);
+            exit(1);
+        },
+    };
     debug!("Config: {:?}", data);
 
     let info;
diff --git a/KubeOS-Rust/kbimg/src/repo.rs b/KubeOS-Rust/kbimg/src/repo.rs
index 3f11d072..752ca403 100644
--- a/KubeOS-Rust/kbimg/src/repo.rs
+++ b/KubeOS-Rust/kbimg/src/repo.rs
@@ -287,8 +287,8 @@ impl RepoInfo {
 // Check pxe config
 fn check_pxe_conf_valid(config: &PxeConfig) -> anyhow::Result<()> {
     if config.dhcp.unwrap_or(false) {
-        if config.local_ip.is_some() || config.net_name.is_some() {
-            bail!("dhcp and local_ip/net_name cannot be set at the same time");
+        if config.local_ip.is_some() || config.net_name.is_some() || config.netmask.is_some() {
+            bail!("dhcp and local_ip/net_name/netmask cannot be set at the same time");
         }
     } else {
         let local_ip = config.local_ip.as_ref().ok_or_else(|| anyhow!("local_ip not found!"))?;
-- 
Gitee