use anyhow::{Context, Result};
use serde_json::json;
use super::*;
use crate::bridge_protocol::{
BridgeManagementPhase, BridgeManagementStatus, BridgeManagementTask,
BridgeManagementTaskPayload, ManagedBridgeReleaseDescriptor, ManagedBridgeSnapshot, now_millis,
};
use crate::storage::Storage;
mod install_ops;
mod service_ops;
pub(super) struct BridgeManagementRunner {
storage: Storage,
task: BridgeManagementTask,
paths: ManagedPaths,
}
impl BridgeManagementRunner {
pub(super) fn new(storage: Storage, task: BridgeManagementTask) -> Self {
let paths = release::managed_paths().unwrap_or_else(|_| {
ManagedPaths::new(
env::var_os("HOME")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("/")),
)
});
Self {
storage,
task,
paths,
}
}
pub(super) fn run(mut self) -> Result<()> {
self.update_task(
BridgeManagementStatus::Running,
BridgeManagementPhase::ResolveTarget,
format!("开始执行远端 bridge {}", self.task.operation.as_str()),
Some("bridge 已接受任务,正在解析本地状态".to_string()),
None,
true,
)?;
let result = match self.task.operation {
crate::bridge_protocol::BridgeManagementOperation::Install
| crate::bridge_protocol::BridgeManagementOperation::Update => {
self.run_install_like(self.task.operation.as_str())
}
crate::bridge_protocol::BridgeManagementOperation::Repair => self.run_repair(),
crate::bridge_protocol::BridgeManagementOperation::Rollback => self.run_rollback(),
crate::bridge_protocol::BridgeManagementOperation::StartService => {
self.run_start_service()
}
crate::bridge_protocol::BridgeManagementOperation::StopService => {
self.run_stop_service()
}
crate::bridge_protocol::BridgeManagementOperation::RestartService => {
self.run_restart_service()
}
crate::bridge_protocol::BridgeManagementOperation::Uninstall => self.run_uninstall(),
};
if let Err(error) = result {
self.fail_task(error)?;
}
Ok(())
}
fn complete_success(&mut self, summary: String, detail: Option<String>) -> Result<()> {
self.update_task(
BridgeManagementStatus::Succeeded,
BridgeManagementPhase::Done,
summary,
detail,
None,
true,
)
}
fn fail_task(&mut self, error: anyhow::Error) -> Result<()> {
let detail = error.to_string();
self.update_task(
BridgeManagementStatus::Failed,
BridgeManagementPhase::Done,
format!("远端 bridge {}失败", self.task.operation.as_str()),
Some(detail.clone()),
Some(failure_code_for_phase(&self.task.phase).to_string()),
true,
)?;
Err(anyhow::anyhow!(detail))
}
fn update_task(
&mut self,
status: BridgeManagementStatus,
phase: BridgeManagementPhase,
summary: String,
detail: Option<String>,
failure_code: Option<String>,
refresh_snapshot: bool,
) -> Result<()> {
self.task.status = status;
self.task.phase = phase;
self.task.summary = summary;
self.task.detail = detail;
self.task.failure_code = failure_code;
self.task.updated_at_ms = now_millis();
if refresh_snapshot {
self.task.snapshot = self.snapshot_now();
if let Some(current_version) = self
.task
.snapshot
.as_ref()
.and_then(|value| value.install_record.as_ref())
.and_then(|record| record.current_version.clone())
{
self.task.current_version = Some(current_version);
}
}
self.storage.upsert_bridge_management_task(&self.task)?;
self.storage.append_event(
"bridge_management_updated",
None,
None,
&json!(BridgeManagementTaskPayload {
task: self.task.clone(),
}),
)?;
Ok(())
}
fn snapshot_now(&self) -> Option<ManagedBridgeSnapshot> {
current_bridge_management_snapshot(self.storage.db_path()).ok()
}
}