summaryrefslogtreecommitdiffhomepage
path: root/test/test-manager/src/vm/update.rs
blob: 1d478b7fa524041e3e3cc22b381dc33324ba2d9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use crate::{
    config::{OsType, PackageType, Provisioner, VmConfig},
    vm::ssh::SSHSession,
};
use anyhow::{Context, Result};
use std::fmt;

#[derive(Debug)]
pub enum Update {
    Logs(Vec<String>),
    Nothing,
}

/// Update system packages in a VM.
///
/// Note that this function is blocking.
pub fn packages(config: &VmConfig, guest_ip: std::net::IpAddr) -> Result<Update> {
    match config.provisioner {
        Provisioner::Noop => return Ok(Update::Nothing),
        Provisioner::Ssh => (),
    }
    // User SSH session to execute package manager update command.
    // This will of course be dependant on the target platform.
    let commands = match (config.os_type, config.package_type) {
        (OsType::Linux, Some(PackageType::Deb)) => {
            Some(vec!["sudo apt-get update", "sudo apt-get upgrade"])
        }
        (OsType::Linux, Some(PackageType::Rpm)) => Some(vec!["sudo dnf update"]),
        (OsType::Linux, _) => None,
        (OsType::Macos | OsType::Windows, _) => None,
    };

    // Issue the update command(s).
    let result = match commands {
        None => {
            log::info!("No update command was found");
            log::debug!(
                "Tried to invoke package update for platform {:?} with package type {:?}",
                config.os_type,
                config.package_type
            );
            Update::Nothing
        }
        Some(commands) => {
            log::info!("retrieving SSH credentials");
            let (username, password) = config.get_ssh_options().context("missing SSH config")?;
            let ssh = SSHSession::connect(username.to_string(), password.to_string(), guest_ip)?;
            let output: Result<Vec<_>> = commands
                .iter()
                .map(|command| {
                    log::info!("Running {command} in guest");
                    ssh.exec_blocking(command)
                })
                .collect();
            Update::Logs(output?)
        }
    };

    Ok(result)
}

// Pretty-printing for an `Update` action.
impl fmt::Display for Update {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Update::Nothing => write!(formatter, "Nothing was updated"),
            Update::Logs(output) => output
                .iter()
                .try_for_each(|output| formatter.write_str(output)),
        }
    }
}