Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion examples/resources/helloworld.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ spec:
resources:
volume:
network:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discard change of this file (helloworld.yaml)

- name: helloworld2
node: ZONE
resources:
volume:
network:
---
apiVersion: v1
kind: Model
Expand All @@ -42,6 +47,24 @@ spec:
hostNetwork: true
containers:
- name: helloworld
image: quay.io/podman/hello:latest
image: sdv.lge.com/bms/blis:1.0
terminationGracePeriodSeconds: 0
restartPolicy: Never
---
apiVersion: v1
kind: Model
metadata:
name: helloworld2
annotations:
io.piccolo.annotations.package-type: helloworld2
io.piccolo.annotations.package-name: helloworld2
io.piccolo.annotations.package-network: default
labels:
app: helloworld2
spec:
hostNetwork: true
containers:
- name: helloworld2
image: sdv.lge.com/bms/frism:1.0
terminationGracePeriodSeconds: 0
restartPolicy: Never
41 changes: 34 additions & 7 deletions src/agent/nodeagent/src/bluechi/filemaker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,55 @@

use common::spec::k8s::Pod;
use std::io::Write;
const SYSTEMD_PATH: &str = "/etc/containers/systemd/";

/// Make files about bluechi for Pod
///
/// ### Parametets
/// * `pods: Vec<Pod>` - Vector of pods
/// ### Description
/// Make `.kube`, `.yaml` files for bluechi
pub async fn make_files_from_pod(pods: Vec<Pod>) -> common::Result<Vec<String>> {
pub async fn make_files_from_pod(pods: Vec<Pod>, node: String) -> common::Result<()> {
let storage_directory = &common::setting::get_config().yaml_storage;
if !std::path::Path::new(storage_directory).exists() {
std::fs::create_dir_all(storage_directory)?;
}

let mut file_names: Vec<String> = Vec::new();

for pod in pods {
file_names.push(pod.get_name());
make_kube_file(storage_directory, &pod.get_name())?;
make_yaml_file(storage_directory, pod)?;
make_yaml_file(storage_directory, pod.clone())?;
delete_symlink(&pod.get_name())
.await
.map_err(|e| format!("Failed to delete symlink for '{}': {}", pod.get_name(), e))?;
make_symlink(&node, &pod.get_name())
.await
.map_err(|e| format!("Failed to create symlink for '{}': {}", pod.get_name(), e))?;
}
Ok(())
}

pub async fn make_symlink(node_name: &str, model_name: &str) -> common::Result<()> {
println!(
"make_symlink_and_reload'{:?}' on host node '{:?}'",
model_name, node_name
);
let original: String = format!(
"{0}/{1}.kube",
common::setting::get_config().yaml_storage,
model_name
);
let link = format!("{}{}.kube", SYSTEMD_PATH, model_name);

Ok(file_names)
let _ = std::os::unix::fs::symlink(original, link)?;

Ok(())
}

pub async fn delete_symlink(model_name: &str) -> common::Result<()> {
// host node
let kube_symlink_path = format!("{}{}.kube", SYSTEMD_PATH, model_name);
let _ = std::fs::remove_file(&kube_symlink_path);

Ok(())
}

/// Make .kube files for Pod
Expand Down
121 changes: 21 additions & 100 deletions src/agent/nodeagent/src/bluechi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,112 +23,33 @@ use common::spec::{
/// Convert `Model` to `Pod`
/// Make `.kube`, `.yaml` files for bluechi
/// Copy files to the guest node running Bluechi
pub async fn parse(package_str: String) -> common::Result<()> {
pub async fn parse(yaml_str: String, nodename: String) -> common::Result<()> {
let (package_str, models_str) = parser::yaml_split(&yaml_str).await?;
let package: Package = serde_yaml::from_str(&package_str)?;

let models: Vec<Model> = parser::get_complete_model(package).await?;
let models: Vec<Model> =
parser::get_complete_model(package, nodename.clone(), models_str).await?;
let pods: Vec<Pod> = models.into_iter().map(Pod::from).collect();

let file_names = filemaker::make_files_from_pod(pods).await?;
filemaker::copy_to_remote_node(file_names)?;
let _ = filemaker::make_files_from_pod(pods, nodename).await?;

Ok(())
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not remove test codes

#[cfg(test)]
mod tests {
use super::*;
use tokio;

// Valid YAML string for testing a Package artifact
fn valid_package_yaml() -> String {
r#"
apiVersion: v1
kind: Package
metadata:
label: null
name: helloworld
spec:
pattern:
- type: plain
models:
- name: helloworld-core
node: HPC
resources:
volume:
network:
"#
.to_string()
}
// filemaker::delete_symlink_and_reload(&mi.get_name(), &model_node)
// .await
// .map_err(|e| {
// format!("Failed to delete symlink for '{}': {}", mi.get_name(), e)
// })?;

// Test case for parsing a valid package YAML
#[tokio::test]
async fn test_parse_success() {
let yaml_str = valid_package_yaml();
let result = parse(yaml_str).await;
assert!(result.is_ok() || result.err().is_some());
}
// make_symlink_and_reload(
// &model_node,
// &mi.get_name(),
// &scenario.get_targets(),
// )
// .await
// .map_err(|e| {
// format!("Failed to create symlink for '{}': {}", mi.get_name(), e)
// })?;

// Test case for parsing an invalid package YAML (syntax error)
#[tokio::test]
async fn test_parse_invalid_yaml_syntax() {
let invalid_yaml = "invalid: ::: yaml";
let result = parse(invalid_yaml.to_string()).await;
assert!(result.is_err(), "parse() unexpectedly succeeded");
}
//filemaker::copy_to_remote_node(file_names)?;

// Test case for parsing a package YAML with missing fields (Missing model)
#[tokio::test]
async fn test_parse_missing_model_field() {
let invalid_yaml = r#"
apiVersion: v1
kind: Package
metadata:
name: helloworld
spec:
pattern:
- type: plain
"#;
let result = parse(invalid_yaml.to_string()).await;
assert!(
result.is_err(),
"parse() unexpectedly succeeded with missing model field"
);
}

// Test case for parsing a package YAML with invalid type in resources (e.g., invalid volume type)
#[tokio::test]
async fn test_parse_invalid_field_type_in_resources() {
let invalid_yaml = r#"
apiVersion: v1
kind: Package
metadata:
name: helloworld
spec:
pattern:
- type: plain
models:
- name: helloworld-core
node: HPC
resources:
volume: 12345 # Invalid type (should be a string, not an integer)
network: vd-network
"#;
let result = parse(invalid_yaml.to_string()).await;
assert!(
result.is_err(),
"parse() unexpectedly succeeded with invalid field type in resources"
);
}

// Test case for parsing an empty YAML string
#[tokio::test]
async fn test_parse_empty_yaml() {
let empty_yaml = "".to_string();
let result = parse(empty_yaml).await;
assert!(
result.is_err(),
"parse() unexpectedly succeeded with empty YAML string"
);
}
Ok(())
}
120 changes: 91 additions & 29 deletions src/agent/nodeagent/src/bluechi/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,54 @@

//! Create Model artifact from given Package information

use common::spec::artifact::{Model, Network, Package, Volume};
use common::spec::artifact::{Artifact, Model, Network, Package, Scenario, Volume};

pub async fn yaml_split(body: &str) -> common::Result<(String, Vec<Model>)> {
let docs: Vec<&str> = body.split("---").collect();
let mut scenario_str = String::new();
let mut package_str = String::new();
let mut models: Vec<Model> = Vec::new();
//let mut network_str = String::new();

for doc in docs {
let value: serde_yaml::Value = serde_yaml::from_str(doc)?;
let artifact_str = serde_yaml::to_string(&value)?;

if let Some(kind) = value.clone().get("kind").and_then(|k| k.as_str()) {
let name: String = match kind {
"Scenario" => serde_yaml::from_value::<Scenario>(value.clone())?.get_name(),
"Package" => serde_yaml::from_value::<Package>(value.clone())?.get_name(),
"Volume" => serde_yaml::from_value::<Volume>(value.clone())?.get_name(),
"Network" => serde_yaml::from_value::<Network>(value.clone())?.get_name(),
"Model" => serde_yaml::from_value::<Model>(value.clone())?.get_name(),
_ => {
println!("unknown artifact");
continue;
}
};

match kind {
"Scenario" => scenario_str = artifact_str,
"Package" => package_str = artifact_str,
"Model" => {
let model = serde_yaml::from_value::<Model>(value)?;
models.push(model);
}
//"Network" => network_str = artifact_str,
_ => continue,
};
}
}

if scenario_str.is_empty() {
Err("There is not any scenario in yaml string".into())
} else if package_str.is_empty() {
//Missing Check is Added for Package
Err("There is not any package in yaml string".into())
} else {
Ok((package_str, models)) //, network_str))
}
}

/// Get combined `Network`, `Volume`, parsed `Model` information
///
Expand All @@ -14,41 +61,56 @@ use common::spec::artifact::{Model, Network, Package, Volume};
/// ### Description
/// Get base `Model` information from package spec
/// Combine `Network`, `Volume`, parsed `Model` information
pub async fn get_complete_model(p: Package) -> common::Result<Vec<Model>> {
let mut models: Vec<Model> = Vec::new();

pub async fn get_complete_model(
p: Package,
node: String,
models: Vec<Model>,
) -> common::Result<Vec<Model>> {
let mut base_models: Vec<Model> = Vec::new();
let mut model_name: String = String::new();
for mi in p.get_models() {
let mut key = format!("Model/{}", mi.get_name());
let base_model_str = common::etcd::get(&key).await?;
let model: Model = serde_yaml::from_str(&base_model_str)?;

if let Some(volume_name) = mi.get_resources().get_volume() {
key = format!("Volume/{}", volume_name);
let volume_str = common::etcd::get(&key).await?;
let volume: Volume = serde_yaml::from_str(&volume_str)?;

if let Some(volume_spec) = volume.get_spec() {
model
.get_podspec()
.volumes
.clone_from(volume_spec.get_volume());
if mi.get_node() == node {
model_name = mi.get_name();
for model in models.iter() {
if model.get_name() == model_name {
base_models.push(model.clone());
} else {
println!("Model {} is not for this node {}", model.get_name(), node);
continue;
}
}
} else {
println!("Model {} is not for this node {}", mi.get_name(), node);
continue;
}
//let mut key = format!("Model/{}", mi.get_name());
//let base_model_str = common::etcd::get(&key).await?;
//let model: Model = serde_yaml::from_str(&base_model_str)?;

if let Some(network_name) = mi.get_resources().get_network() {
key = format!("Network/{}", network_name);
let network_str = common::etcd::get(&key).await?;
let network: Network = serde_yaml::from_str(&network_str)?;
// if let Some(volume_name) = mi.get_resources().get_volume() {
// key = format!("Volume/{}", volume_name);
// let volume_str: String = common::etcd::get(&key).await?;
// let volume: Volume = serde_yaml::from_str(&volume_str)?;

if let Some(network_spec) = network.get_spec() {
// TODO
}
}
// if let Some(volume_spec) = volume.get_spec() {
// model
// .get_podspec()
// .volumes
// .clone_from(volume_spec.get_volume());
// }
// }

models.push(model);
}
// if let Some(network_name) = mi.get_resources().get_network() {
// key = format!("Network/{}", network_name);
// let network_str = common::etcd::get(&key).await?;
// let network: Network = serde_yaml::from_str(&network_str)?;

Ok(models)
// if let Some(network_spec) = network.get_spec() {
// // TODO
// }
//}
}
Ok(base_models)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not remove test codes

//UNIT TEST CASES
Expand Down
Loading
Loading