use crate::{
bos::{self, template::http_client::v2::types::BosSessionTemplate},
cfs::{
self,
configuration::http_client::v2::types::cfs_configuration_response::CfsConfigurationResponse,
session::http_client::v2::types::CfsSessionGetResponse,
},
common::{self, gitea},
error::Error,
hsm,
ims::{self, image::http_client::types::Image},
};
use chrono::NaiveDateTime;
use globset::Glob;
use serde_json::Value;
use super::http_client::{
v2::types::cfs_configuration_request::CfsConfigurationRequest,
v3::types::{
cfs_configuration::LayerDetails, cfs_configuration_response::Layer,
},
};
pub async fn create_new_configuration(
shasta_token: &str,
shasta_base_url: &str,
shasta_root_cert: &[u8],
configuration: &CfsConfigurationRequest,
configuration_name: &str,
overwrite: bool,
) -> Result<CfsConfigurationResponse, Error> {
log::info!("Check CFS configuration '{}' exists", configuration_name);
let cfs_configuration_vec = crate::cfs::configuration::http_client::v2::get(
shasta_token,
shasta_base_url,
shasta_root_cert,
Some(configuration_name),
)
.await
.map_err(|e| Error::Message(e.to_string()))
.unwrap_or_default();
if !cfs_configuration_vec.is_empty() {
if overwrite {
log::info!(
"CFS configuration '{}' already exists but 'overwrite' has been enabled",
configuration_name
);
} else {
log::warn!(
"CFS configuration '{}' already exists, cancel the process",
configuration_name
);
return Err(Error::ConfigurationAlreadyExists(
configuration_name.to_string(),
));
}
}
log::info!(
"CFS configuration '{}' does not exists, creating new CFS configuration",
configuration_name
);
crate::cfs::configuration::http_client::v2::put(
shasta_token,
shasta_base_url,
shasta_root_cert,
&configuration.clone().into(),
configuration_name,
)
.await
.map(|config| config.into())
.map_err(|e| Error::Message(e.to_string()))
}
pub fn filter_3(
cfs_configuration_vec: &mut Vec<CfsConfigurationResponse>,
configuration_name_pattern_opt: Option<&str>,
limit_number_opt: Option<&u8>,
since_opt: Option<NaiveDateTime>,
until_opt: Option<NaiveDateTime>,
) -> Result<Vec<CfsConfigurationResponse>, Error> {
log::info!("Filter CFS configurations");
if let (Some(since), Some(until)) = (since_opt, until_opt) {
cfs_configuration_vec.retain(|cfs_configuration| {
let date =
chrono::DateTime::parse_from_rfc3339(&cfs_configuration.last_updated)
.unwrap()
.naive_utc();
since <= date && date < until
});
}
if let Some(configuration_name_pattern) = configuration_name_pattern_opt {
let glob = Glob::new(configuration_name_pattern)
.unwrap()
.compile_matcher();
cfs_configuration_vec.retain(|cfs_configuration| {
glob.is_match(cfs_configuration.name.clone())
});
}
cfs_configuration_vec.sort_by(|cfs_configuration_1, cfs_configuration_2| {
cfs_configuration_1
.last_updated
.cmp(&cfs_configuration_2.last_updated)
});
if let Some(limit_number) = limit_number_opt {
*cfs_configuration_vec = cfs_configuration_vec[cfs_configuration_vec
.len()
.saturating_sub(*limit_number as usize)..]
.to_vec();
}
Ok(cfs_configuration_vec.to_vec())
}
pub async fn filter(
shasta_token: &str,
shasta_base_url: &str,
shasta_root_cert: &[u8],
cfs_configuration_vec: &mut Vec<CfsConfigurationResponse>,
configuration_name_pattern_opt: Option<&str>,
hsm_group_name_vec: &[String],
since_opt: Option<NaiveDateTime>,
until_opt: Option<NaiveDateTime>,
limit_number_opt: Option<&u8>,
) -> Result<Vec<CfsConfigurationResponse>, Error> {
log::info!("Filter CFS configurations");
let xname_from_groups_vec =
hsm::group::utils::get_member_vec_from_hsm_name_vec(
shasta_token,
shasta_base_url,
shasta_root_cert,
hsm_group_name_vec,
)
.await?;
let (mut cfs_session_vec, mut bos_sessiontemplate_vec, cfs_component_vec) = tokio::try_join!(
cfs::session::http_client::v2::get_all(
shasta_token,
shasta_base_url,
shasta_root_cert,
),
bos::template::http_client::v2::get_all(
shasta_token,
shasta_base_url,
shasta_root_cert,
),
cfs::component::http_client::v2::get_parallel(
shasta_token,
shasta_base_url,
shasta_root_cert,
&xname_from_groups_vec,
),
)?;
bos::template::utils::filter(
&mut bos_sessiontemplate_vec,
hsm_group_name_vec,
&xname_from_groups_vec,
None,
);
cfs::session::utils::filter_by_hsm(
shasta_token,
shasta_base_url,
shasta_root_cert,
&mut cfs_session_vec,
hsm_group_name_vec,
None,
true,
)
.await?;
let image_id_cfs_configuration_target_from_bos_sessiontemplate: Vec<(
String,
String,
Vec<String>,
)> = bos::template::utils::get_image_id_cfs_configuration_target_tuple_vec(
bos_sessiontemplate_vec,
);
let image_id_cfs_configuration_target_from_cfs_session: Vec<(
String,
String,
Vec<String>,
)> = cfs::session::utils::get_image_id_cfs_configuration_target_tuple_vec(
cfs_session_vec,
);
let desired_config_vec: Vec<String> = cfs_component_vec
.into_iter()
.map(|cfs_component| cfs_component.desired_config.unwrap())
.collect();
let cfs_configuration_in_cfs_session_and_bos_sessiontemplate: Vec<String> = [
image_id_cfs_configuration_target_from_bos_sessiontemplate
.into_iter()
.map(|(_, config, _)| config)
.collect(),
image_id_cfs_configuration_target_from_cfs_session
.into_iter()
.map(|(_, config, _)| config)
.collect(),
desired_config_vec,
]
.concat();
cfs_configuration_vec.retain(|cfs_configuration| {
hsm_group_name_vec
.iter()
.any(|hsm_group| cfs_configuration.name.contains(hsm_group))
|| cfs_configuration_in_cfs_session_and_bos_sessiontemplate
.contains(&cfs_configuration.name)
});
if let (Some(since), Some(until)) = (since_opt, until_opt) {
cfs_configuration_vec.retain(|cfs_configuration| {
let date =
chrono::DateTime::parse_from_rfc3339(&cfs_configuration.last_updated)
.unwrap()
.naive_utc();
since <= date && date < until
});
}
cfs_configuration_vec.sort_by(|cfs_configuration_1, cfs_configuration_2| {
cfs_configuration_1
.last_updated
.cmp(&cfs_configuration_2.last_updated)
});
if let Some(configuration_name_pattern) = configuration_name_pattern_opt {
let glob = Glob::new(configuration_name_pattern)
.unwrap()
.compile_matcher();
cfs_configuration_vec.retain(|cfs_configuration| {
glob.is_match(cfs_configuration.name.clone())
});
}
if let Some(limit_number) = limit_number_opt {
*cfs_configuration_vec = cfs_configuration_vec[cfs_configuration_vec
.len()
.saturating_sub(*limit_number as usize)..]
.to_vec();
}
Ok(cfs_configuration_vec.to_vec())
}
pub async fn get_and_filter(
shasta_token: &str,
shasta_base_url: &str,
shasta_root_cert: &[u8],
configuration_name: Option<&str>,
configuration_name_pattern: Option<&str>,
hsm_group_name_vec: &[String],
since_opt: Option<NaiveDateTime>,
until_opt: Option<NaiveDateTime>,
limit_number_opt: Option<&u8>,
) -> Result<Vec<CfsConfigurationResponse>, Error> {
let mut cfs_configuration_vec: Vec<CfsConfigurationResponse> =
cfs::configuration::http_client::v2::get(
shasta_token,
shasta_base_url,
shasta_root_cert,
configuration_name,
)
.await?;
if !common::jwt_ops::is_user_admin(shasta_token) {
cfs::configuration::utils::filter(
shasta_token,
shasta_base_url,
shasta_root_cert,
&mut cfs_configuration_vec,
configuration_name_pattern,
hsm_group_name_vec,
since_opt,
until_opt,
limit_number_opt,
)
.await?;
}
Ok(cfs_configuration_vec)
}
pub async fn get_derivatives(
shasta_token: &str,
shasta_base_url: &str,
shasta_root_cert: &[u8],
configuration_name: &str,
) -> Result<
(
Option<Vec<CfsSessionGetResponse>>,
Option<Vec<BosSessionTemplate>>,
Option<Vec<Image>>,
),
Error,
> {
let mut image_id_vec: Vec<String> = Vec::new();
let (mut cfs_session_vec, mut bos_sessiontemplate_vec, mut ims_image_vec) = tokio::try_join!(
cfs::session::http_client::v2::get_all(
shasta_token,
shasta_base_url,
shasta_root_cert
),
bos::template::http_client::v2::get_all(
shasta_token,
shasta_base_url,
shasta_root_cert
),
ims::image::http_client::get_all(
shasta_token,
shasta_base_url,
shasta_root_cert
)
)?;
cfs::session::utils::filter_by_cofiguration(
&mut cfs_session_vec,
configuration_name,
);
bos_sessiontemplate_vec.retain(|bos_sessiontemplate| {
bos_sessiontemplate
.get_image_vec()
.iter()
.any(|image_id_aux| image_id_vec.contains(image_id_aux))
|| bos_sessiontemplate.get_configuration().unwrap_or_default()
== configuration_name
});
image_id_vec.extend(
cfs_session_vec
.iter()
.flat_map(|cfs_session| cfs_session.get_result_id_vec().into_iter()),
);
image_id_vec.extend(
bos_sessiontemplate_vec
.iter()
.flat_map(|bos_sessiontemplate| bos_sessiontemplate.get_image_vec()),
);
ims_image_vec
.retain(|image| image_id_vec.contains(image.id.as_ref().unwrap()));
Ok((
Some(cfs_session_vec),
Some(bos_sessiontemplate_vec),
Some(ims_image_vec),
))
}
pub async fn get_configuration_layer_details(
shasta_root_cert: &[u8],
gitea_base_url: &str,
gitea_token: &str,
layer: Layer,
site_name: &str,
) -> Result<LayerDetails, Error> {
let commit_id: String =
layer.commit.clone().unwrap_or("Not defined".to_string());
let mut branch_name_vec: Vec<String> = Vec::new();
let mut tag_name_vec: Vec<String> = Vec::new();
let commit_sha;
let repo_ref_vec_rslt = gitea::http_client::get_all_refs_from_repo_url(
gitea_base_url,
gitea_token,
&layer.clone_url,
shasta_root_cert,
)
.await;
let repo_ref_vec = match repo_ref_vec_rslt {
Ok(value) => value,
Err(error) => {
log::warn!(
"Could not fetch repo '{}' refs. Reason:\n{:#?}",
layer.clone_url,
error
);
vec![]
}
};
let mut ref_value_vec: Vec<&Value> = repo_ref_vec
.iter()
.filter(|repo_ref| {
repo_ref
.pointer("/object/sha")
.unwrap()
.as_str()
.unwrap()
.eq(&commit_id)
})
.collect();
if ref_value_vec.len() == 1 {
let ref_value = ref_value_vec.first().unwrap();
log::debug!("Found ref in remote git repo:\n{:#?}", ref_value);
let ref_type: &str =
ref_value.pointer("/object/type").unwrap().as_str().unwrap();
let mut r#ref = ref_value["ref"].as_str().unwrap().split("/").skip(1);
let _ref_1 = r#ref.next();
let ref_2 = r#ref.next();
if ref_type == "tag" {
let tag_name = ref_2.unwrap();
let commit_sha_value = gitea::http_client::get_commit_from_tag(
ref_value["url"].as_str().unwrap(),
&tag_name,
gitea_token,
shasta_root_cert,
site_name,
)
.await?;
commit_sha = commit_sha_value
.pointer("/commit/sha")
.unwrap()
.as_str()
.unwrap();
let annotated_tag_commit_sha =
[commit_id.clone(), commit_sha.to_string()];
ref_value_vec = repo_ref_vec
.iter()
.filter(|repo_ref| {
let ref_sha: String = repo_ref
.pointer("/object/sha")
.unwrap()
.as_str()
.unwrap()
.to_string();
annotated_tag_commit_sha.contains(&ref_sha)
})
.collect();
}
}
for ref_value in ref_value_vec {
log::debug!("Found ref in remote git repo:\n{:#?}", ref_value);
let ref_type: &str =
ref_value.pointer("/object/type").unwrap().as_str().unwrap();
let mut r#ref = ref_value["ref"].as_str().unwrap().split("/").skip(1);
let ref_1 = r#ref.next();
let ref_2 = r#ref.collect::<Vec<_>>().join("/");
if ref_type == "commit" {
if let (Some("heads"), branch_name_aux) = (ref_1, ref_2.clone()) {
branch_name_vec.push(branch_name_aux);
} else if let (Some("tags"), tag_name_aux) = (ref_1, ref_2) {
tag_name_vec.push(tag_name_aux);
}
} else {
tag_name_vec.push(ref_2);
}
}
if let Some(cfs_config_layer_branch) = &layer.branch {
branch_name_vec.push(cfs_config_layer_branch.to_string());
}
let commit_id_opt = layer.commit.as_ref();
let gitea_commit_details: serde_json::Value =
if let Some(commit_id) = commit_id_opt {
let repo_name = layer
.clone_url
.trim_start_matches("https://api-gw-service-nmn.local/vcs/")
.trim_end_matches(".git");
gitea::http_client::get_commit_details_from_external_url(
repo_name,
commit_id,
gitea_token,
shasta_root_cert,
site_name,
)
.await?
} else {
serde_json::json!({})
};
Ok(LayerDetails::new(
&layer.name,
layer
.clone_url
.trim_start_matches(
format!("https://api.cmn.{}.cscs.ch", site_name).as_str(),
)
.trim_end_matches(".git"),
&commit_id,
gitea_commit_details
.pointer("/commit/committer/name")
.unwrap_or(&serde_json::json!("Not defined"))
.as_str()
.unwrap(),
gitea_commit_details
.pointer("/commit/committer/date")
.unwrap_or(&serde_json::json!("Not defined"))
.as_str()
.unwrap(),
&branch_name_vec.join(","),
&tag_name_vec.join(","),
&layer.playbook,
))
}