use anyhow::{Context, Error, bail};
use crate::common::{self, app_context::AppContext};
use crossterm::style::Stylize;
use manta_backend_dispatcher::{
interfaces::apply_sat_file::SatTrait,
types::{K8sAuth, K8sDetails},
};
use serde_yaml::Value;
use crate::{
cli::commands::apply_sat_file::utils,
common::vault::http_client::fetch_shasta_k8s_secrets_from_vault,
};
use manta_backend_dispatcher::interfaces::hsm::group::GroupTrait;
pub struct SatApplyOptions<'a> {
pub vault_base_url: &'a str,
pub k8s_api_url: &'a str,
pub sat_file_content: &'a str,
pub values_file_content_opt: Option<&'a str>,
pub values_cli_opt: Option<&'a [String]>,
pub ansible_verbosity_opt: Option<u8>,
pub ansible_passthrough_opt: Option<&'a str>,
pub reboot: bool,
pub watch_logs: bool,
pub timestamps: bool,
pub prehook_opt: Option<&'a str>,
pub posthook_opt: Option<&'a str>,
pub image_only: bool,
pub session_template_only: bool,
pub debug_on_failure: bool,
pub overwrite: bool,
pub dry_run: bool,
pub assume_yes: bool,
pub k8s: &'a K8sDetails,
}
fn validate_hook(
hook_opt: Option<&str>,
label: &str,
) -> Result<(), Error> {
if let Some(hook) = hook_opt {
crate::common::hooks::check_hook_perms(hook_opt)
.map_err(|e| anyhow::anyhow!("{}. File: {}", e, hook))?;
println!(
"{}-hook script '{}' exists and is executable.",
label, hook
);
}
Ok(())
}
fn run_hook_if_present(
hook_opt: Option<&str>,
label: &str,
) -> Result<(), Error> {
if let Some(hook) = hook_opt {
println!("Running the {}-hook '{}'", label, hook);
let code = crate::common::hooks::run_hook(hook_opt)?;
tracing::debug!("{}-hook script completed ok. RT={}", label, code);
}
Ok(())
}
pub async fn exec(
ctx: &AppContext<'_>,
token: &str,
opts: &SatApplyOptions<'_>,
) -> Result<(), Error> {
let backend = ctx.infra.backend;
let site_name = ctx.infra.site_name;
let shasta_base_url = ctx.infra.shasta_base_url;
let shasta_root_cert = ctx.infra.shasta_root_cert;
let gitea_base_url = ctx.infra.gitea_base_url;
let gitea_token = crate::common::vault::http_client::fetch_shasta_vcs_token(
token,
opts.vault_base_url,
site_name,
)
.await?;
let hsm_group_available_vec =
backend.get_group_name_available(token).await?;
validate_hook(opts.prehook_opt, "Pre")?;
validate_hook(opts.posthook_opt, "Post")?;
tracing::info!("Render SAT template file");
let sat_template_file_yaml: Value = utils::render_jinja2_sat_file_yaml(
opts.sat_file_content,
opts.values_file_content_opt,
opts.values_cli_opt,
)?;
let sat_template_file_string = serde_yaml::to_string(&sat_template_file_yaml)
.context(
"Failed to serialize SAT template file \
to YAML string",
)?;
let mut sat_template: utils::SatFile =
serde_yaml::from_str(&sat_template_file_string).context(
"Could not parse SAT template yaml \
file",
)?;
sat_template.filter(opts.image_only, opts.session_template_only)?;
let sat_template_file_yaml: Value = serde_yaml::to_value(sat_template)
.context(
"Failed to convert SAT template to \
YAML value",
)?;
println!(
"{}\n{}",
"#### SAT file content ####".blue(),
serde_yaml::to_string(&sat_template_file_yaml).context(
"Failed to serialize SAT template to \
YAML for display",
)?,
);
if !common::user_interaction::confirm(
"Please check the template above and \
confirm to proceed.",
opts.assume_yes,
) {
bail!("Operation cancelled by user");
}
if sat_template_file_yaml.get("session_templates").is_some()
&& opts.reboot
&& !common::user_interaction::confirm(
"This operation will reboot nodes. \
Please confirm to proceed.",
opts.assume_yes,
)
{
println!("Operation cancelled by user");
return Ok(());
}
run_hook_if_present(opts.prehook_opt, "pre")?;
let shasta_k8s_secrets = match &opts.k8s.authentication {
K8sAuth::Native {
certificate_authority_data,
client_certificate_data,
client_key_data,
} => {
serde_json::json!({
"certificate-authority-data":
certificate_authority_data,
"client-certificate-data":
client_certificate_data,
"client-key-data":
client_key_data
})
}
K8sAuth::Vault { base_url } => {
fetch_shasta_k8s_secrets_from_vault(base_url, site_name, token)
.await
.context("Failed to fetch K8s secrets from Vault")?
}
};
backend
.apply_sat_file(
token,
shasta_base_url,
shasta_root_cert,
opts.vault_base_url,
site_name,
opts.k8s_api_url,
shasta_k8s_secrets,
sat_template_file_yaml,
&hsm_group_available_vec,
opts.ansible_verbosity_opt,
opts.ansible_passthrough_opt,
gitea_base_url,
&gitea_token,
opts.reboot,
opts.watch_logs,
opts.timestamps,
opts.debug_on_failure,
opts.overwrite,
opts.dry_run,
)
.await?;
run_hook_if_present(opts.posthook_opt, "post")?;
Ok(())
}