2024-11-24 00:54:19 +01:00
|
|
|
use anyhow::Context;
|
|
|
|
|
2024-11-24 00:53:43 +01:00
|
|
|
use super::agent_state::AgentState;
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct AgentRefresh {
|
|
|
|
_state: AgentState,
|
|
|
|
host: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AgentRefresh {
|
|
|
|
pub fn new(state: impl Into<AgentState>, host: impl Into<String>) -> Self {
|
|
|
|
Self {
|
|
|
|
_state: state.into(),
|
|
|
|
host: host.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait::async_trait]
|
|
|
|
impl notmad::Component for AgentRefresh {
|
|
|
|
async fn run(
|
|
|
|
&self,
|
|
|
|
cancellation_token: tokio_util::sync::CancellationToken,
|
|
|
|
) -> Result<(), notmad::MadError> {
|
2024-11-24 01:13:09 +01:00
|
|
|
let cancel = nodrift::schedule_drifter(std::time::Duration::from_secs(60), self.clone());
|
2024-11-24 00:53:43 +01:00
|
|
|
tokio::select! {
|
|
|
|
_ = cancel.cancelled() => {},
|
|
|
|
_ = cancellation_token.cancelled() => {
|
|
|
|
tracing::debug!("cancelling agent refresh");
|
|
|
|
cancel.cancel();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait::async_trait]
|
|
|
|
impl nodrift::Drifter for AgentRefresh {
|
|
|
|
async fn execute(&self, _token: tokio_util::sync::CancellationToken) -> anyhow::Result<()> {
|
|
|
|
tracing::info!(host = self.host, "refreshing agent");
|
|
|
|
|
|
|
|
// Get plan
|
|
|
|
let plan = Plan::new();
|
|
|
|
let tasks = plan.tasks().await?;
|
|
|
|
|
|
|
|
// For task
|
|
|
|
for task in tasks {
|
|
|
|
// Check idempotency rules
|
|
|
|
if !task.should_run().await? {
|
|
|
|
tracing::debug!(task = task.id(), "skipping run");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run task if not valid
|
|
|
|
tracing::info!(task = task.id(), "executing task");
|
|
|
|
task.execute().await?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Plan {}
|
|
|
|
impl Plan {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn tasks(&self) -> anyhow::Result<Vec<Task>> {
|
|
|
|
Ok(vec![Task::new()])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Task {}
|
|
|
|
|
|
|
|
impl Task {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn id(&self) -> String {
|
|
|
|
"apt".into()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn should_run(&self) -> anyhow::Result<bool> {
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn execute(&self) -> anyhow::Result<()> {
|
2024-11-24 01:13:09 +01:00
|
|
|
let mut cmd = tokio::process::Command::new("apt-get");
|
|
|
|
cmd.args(["update", "-q"]);
|
2024-11-24 00:54:19 +01:00
|
|
|
let output = cmd.output().await.context("failed to run apt update")?;
|
2024-11-24 00:53:43 +01:00
|
|
|
match output.status.success() {
|
|
|
|
true => tracing::info!("successfully ran apt update"),
|
|
|
|
false => {
|
|
|
|
anyhow::bail!(
|
|
|
|
"failed to run apt update: {}",
|
|
|
|
std::str::from_utf8(&output.stderr)?
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-24 01:13:09 +01:00
|
|
|
let mut cmd = tokio::process::Command::new("apt-get");
|
2024-11-24 00:53:43 +01:00
|
|
|
cmd.env("DEBIAN_FRONTEND", "noninteractive")
|
2024-11-24 01:13:09 +01:00
|
|
|
.args(["upgrade", "-y"]);
|
2024-11-24 00:54:19 +01:00
|
|
|
let output = cmd.output().await.context("failed to run apt upgrade")?;
|
2024-11-24 00:53:43 +01:00
|
|
|
match output.status.success() {
|
|
|
|
true => tracing::info!("successfully ran apt upgrade"),
|
|
|
|
false => {
|
|
|
|
anyhow::bail!(
|
|
|
|
"failed to run apt upgrade: {}",
|
|
|
|
std::str::from_utf8(&output.stderr)?
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|