feat: add persistence for last deployment path in Nginx handler
This commit is contained in:
@@ -56,6 +56,14 @@ pub trait FsHandler: Send + Sync + 'static {
|
||||
// clean up old config files that are applied to nginx
|
||||
// keep only latest n deployments.
|
||||
async fn cleanup_config(&self, n: usize) -> Result<()>;
|
||||
|
||||
// Persist the root config path of the last successful deployment.
|
||||
// Survives agent restarts so Reload/Test commands work without a new ConfigUpdate.
|
||||
async fn save_last_deployment(&self, root_config_path: &str) -> Result<()>;
|
||||
|
||||
// Load the last persisted root config path, if any.
|
||||
// Returns Ok(None) when no state file exists or it is empty/corrupt.
|
||||
async fn load_last_deployment(&self) -> Result<Option<String>>;
|
||||
}
|
||||
|
||||
pub struct FsHandlerImpl {
|
||||
@@ -91,6 +99,10 @@ impl FsHandlerImpl {
|
||||
self.get_deployment_dir().join(deployment_id)
|
||||
}
|
||||
|
||||
fn get_state_file_path(&self) -> std::path::PathBuf {
|
||||
std::path::Path::new(&self.settings.nginx_config_path).join(".last_deployment")
|
||||
}
|
||||
|
||||
async fn get_deployment_config_path(
|
||||
&self,
|
||||
deployment_id: &str,
|
||||
@@ -183,6 +195,58 @@ impl FsHandler for FsHandlerImpl {
|
||||
Ok(full_output_path.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
async fn save_last_deployment(&self, root_config_path: &str) -> Result<()> {
|
||||
let state_path = self.get_state_file_path();
|
||||
let tmp_path = state_path.with_extension("tmp");
|
||||
tokio::fs::write(&tmp_path, format!("{}\n", root_config_path)).await?;
|
||||
tokio::fs::rename(&tmp_path, &state_path).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn load_last_deployment(&self) -> Result<Option<String>> {
|
||||
// primary: try state file
|
||||
let state_path = self.get_state_file_path();
|
||||
if state_path.exists() {
|
||||
let content = tokio::fs::read_to_string(&state_path).await?;
|
||||
let path = content.trim().to_string();
|
||||
if !path.is_empty() {
|
||||
return Ok(Some(path));
|
||||
}
|
||||
}
|
||||
|
||||
// fallback: scan deployments directory for the newest deployment
|
||||
let deployment_dir = self.get_deployment_dir();
|
||||
if !deployment_dir.exists() {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut entries = tokio::fs::read_dir(&deployment_dir).await?;
|
||||
let mut candidates: Vec<(std::path::PathBuf, std::time::SystemTime)> = Vec::new();
|
||||
while let Some(entry) = entries.next_entry().await? {
|
||||
if entry.file_type().await.map_or(false, |t| t.is_dir()) {
|
||||
if let Ok(mtime) = entry.metadata().await.and_then(|m| m.modified()) {
|
||||
candidates.push((entry.path(), mtime));
|
||||
}
|
||||
}
|
||||
}
|
||||
// sort descending by mtime (newest first)
|
||||
candidates.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
|
||||
for (dir, _) in &candidates {
|
||||
let mut dir_entries = tokio::fs::read_dir(dir).await?;
|
||||
while let Some(file) = dir_entries.next_entry().await? {
|
||||
if file.file_type().await.map_or(false, |t| t.is_file()) {
|
||||
let name = file.file_name().to_string_lossy().to_string();
|
||||
if name == "nginx.conf" || name.ends_with(".conf") {
|
||||
let path = file.path().to_string_lossy().to_string();
|
||||
return Ok(Some(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn cleanup_config(&self, n: usize) -> Result<()> {
|
||||
let deployment_dir = self.get_deployment_dir();
|
||||
// loop through all files in the deployment dir and delete them
|
||||
|
||||
Reference in New Issue
Block a user