1use anyhow::Context as AnyhowContext;
16
17use std::fs::{File, create_dir_all, remove_dir_all};
18use std::io::{BufWriter, copy};
19use std::path::PathBuf;
20use std::str::FromStr;
21
22use chrono::{DateTime, Utc};
23use kube::{Api, Client};
24use mz_cloud_resources::crd::materialize::v1alpha1::Materialize;
25use mz_server_core::listeners::AuthenticatorKind;
26use zip::ZipWriter;
27use zip::write::SimpleFileOptions;
28
29use crate::{AuthMode, PasswordAuthCredentials};
30
31pub fn format_base_path(date_time: DateTime<Utc>) -> PathBuf {
33 PathBuf::from(format!("mz_debug_{}", date_time.format("%Y-%m-%dT%H:%MZ")))
34}
35
36pub fn validate_pg_connection_string(connection_string: &str) -> Result<String, String> {
37 tokio_postgres::Config::from_str(connection_string)
38 .map(|_| connection_string.to_string())
39 .map_err(|e| format!("Invalid PostgreSQL connection string: {}", e))
40}
41
42pub fn create_tracing_log_file(dir: PathBuf) -> Result<File, std::io::Error> {
43 let log_file = dir.join("tracing.log");
44 if log_file.exists() {
45 remove_dir_all(&log_file)?;
46 }
47 create_dir_all(&dir)?;
48 let file = File::create(&log_file);
49 file
50}
51
52pub fn zip_debug_folder(zip_file_name: PathBuf, folder_path: &PathBuf) -> std::io::Result<()> {
54 if zip_file_name.exists() {
56 std::fs::remove_file(&zip_file_name)?;
57 }
58 let zip_file = File::create(&zip_file_name)?;
59 let mut zip_writer = ZipWriter::new(BufWriter::new(zip_file));
60
61 for entry in walkdir::WalkDir::new(folder_path) {
62 let entry = entry?;
63 let path = entry.path();
64
65 if path.is_file() {
66 zip_writer.start_file(path.to_string_lossy(), SimpleFileOptions::default())?;
67 let mut file = File::open(path)?;
68 copy(&mut file, &mut zip_writer)?;
69 }
70 }
71
72 zip_writer.finish()?;
73 Ok(())
74}
75
76pub async fn get_k8s_auth_mode(
77 mz_username: Option<String>,
78 mz_password: Option<String>,
79 k8s_client: &Client,
80 k8s_namespace: &String,
81 mz_instance_name: &String,
82) -> Result<AuthMode, anyhow::Error> {
83 let materialize_api = Api::<Materialize>::namespaced(k8s_client.clone(), k8s_namespace);
84
85 let materialize_cr = materialize_api
86 .get(mz_instance_name)
87 .await
88 .with_context(|| {
89 format!(
90 "Could not find Materialize CR with name: {}",
91 mz_instance_name
92 )
93 })?;
94
95 let authenticator_kind = materialize_cr.spec.authenticator_kind;
96
97 match authenticator_kind {
98 AuthenticatorKind::None => Ok(AuthMode::None),
99 AuthenticatorKind::Password => {
100 if let (Some(mz_username), Some(mz_password)) = (&mz_username, &mz_password) {
101 Ok(AuthMode::Password(PasswordAuthCredentials {
102 username: mz_username.clone(),
103 password: mz_password.clone(),
104 }))
105 } else {
106 Err(anyhow::anyhow!(
107 "mz_username and mz_password are required for password authentication"
108 ))
109 }
110 }
111 _ => Err(anyhow::anyhow!(
112 "Unsupported authenticator kind: {:?}",
113 authenticator_kind
114 )),
115 }
116}