apt_swarm/plumbing/
update.rs

1use crate::args::ContainerUpdateCheck;
2use crate::errors::*;
3use serde::{Deserialize, Serialize};
4use tokio::process::Command;
5
6#[derive(Debug, PartialEq, Serialize, Deserialize)]
7pub struct CraneOutput {
8    pub config: ContainerConfig,
9}
10
11#[derive(Debug, PartialEq, Serialize, Deserialize)]
12pub struct ContainerConfig {
13    #[serde(rename = "Env")]
14    pub env: Vec<String>,
15}
16
17pub enum Updates {
18    Available { current: String, latest: String },
19    AlreadyLatest { commit: String },
20}
21
22pub async fn check(update: &ContainerUpdateCheck) -> Result<Updates> {
23    debug!("Checking updates for container image: {:?}", update.image);
24
25    if update.commit.is_empty() {
26        bail!(
27            "The currently running commit is not configured: {:?}",
28            update.commit
29        );
30    }
31
32    let output = Command::new("crane")
33        .arg("config")
34        .arg("--")
35        .arg(&update.image)
36        .output()
37        .await
38        .context("Failed to execute crane")?;
39
40    if !output.stderr.is_empty() {
41        let error = String::from_utf8_lossy(&output.stderr);
42        warn!("Crane stderr was non-emtpy: {:?}", error);
43    }
44
45    if !output.status.success() {
46        bail!("Crane exited with error: {:?}", output.status);
47    }
48
49    let output = serde_json::from_slice::<CraneOutput>(&output.stdout)
50        .context("Failed to deserialize crane output")?;
51
52    for env in &output.config.env {
53        trace!("Found environment variable in container image: {:?}", env);
54        if let Some(commit) = env.strip_prefix("UPDATE_CHECK_COMMIT=") {
55            debug!("Found commit in container image: {commit:?}");
56            if commit == update.commit {
57                debug!(
58                    "Update check detected we're running the latest version of {:?} (commit={:?}",
59                    update.image, commit
60                );
61                return Ok(Updates::AlreadyLatest {
62                    commit: commit.to_string(),
63                });
64            } else {
65                debug!("Update check detected we're running an outdated version of {:?} (current={:?}, latest={:?})",
66                    update.image,
67                    update.commit, commit
68                );
69                return Ok(Updates::Available {
70                    current: update.commit.to_string(),
71                    latest: commit.to_string(),
72                });
73            }
74        }
75    }
76
77    bail!(
78        "Failed to detect commit id in specified container image: {:?}",
79        update.image
80    );
81}