cubic 0.14.0

Cubic is a lightweight command line manager for virtual machines. It has a simple, daemon-less and rootless design. All Cubic virtual machines run isolated in the user context. Cubic is built on top of QEMU, KVM and cloud-init. Show all supported images: $ cubic images Create a new virtual machine instance: $ cubic create mymachine --image ubuntu:noble List all virtual machine instances: $ cubic instances Start an instance: $ cubic start <instance name> Stop an instance: $ cubic stop <instance name> Open a shell in the instance: $ cubic ssh <machine name> Copy a file from the host to the instance: $ cubic scp <path/to/host/file> <machine>:<path/to/guest/file> Copy a file from the instance to the hots: $ cubic scp <machine>:<path/to/guest/file> <path/to/host/file>
use crate::error::Error;
use crate::instance::{InstanceName, InstanceStore};
use crate::util;
use crate::view::{Console, MapView};
use clap::Parser;

/// Show a virtual machine instance
#[derive(Parser)]
pub struct InstanceShowCommand {
    /// Name of the virtual machine instance
    pub instance: InstanceName,
}

impl InstanceShowCommand {
    pub fn run(
        &self,
        console: &mut dyn Console,
        instance_store: &dyn InstanceStore,
    ) -> Result<(), Error> {
        if !instance_store.exists(self.instance.as_str()) {
            return Result::Err(Error::UnknownInstance(self.instance.to_string()));
        }

        let instance = instance_store.load(self.instance.as_str())?;

        let mut view = MapView::new();
        view.add("Arch", &instance.arch.to_string());
        view.add("CPUs", &instance.cpus.to_string());
        view.add("Memory", &util::bytes_to_human_readable(instance.mem));
        view.add(
            "Disk",
            &util::bytes_to_human_readable(instance.disk_capacity),
        );
        view.add("User", &instance.user);
        view.add("SSH Port", &instance.ssh_port.to_string());

        for (index, rule) in instance.hostfwd.iter().enumerate() {
            let key = if index == 0 { "Forward" } else { "" };
            view.add(key, &rule.to_string());
        }

        view.print(console);

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::arch::Arch;
    use crate::instance::instance_store_mock::tests::InstanceStoreMock;
    use crate::instance::Instance;
    use crate::view::console_mock::tests::ConsoleMock;
    use std::str::FromStr;

    #[test]
    fn test_show_command() {
        let console = &mut ConsoleMock::new();
        let instance_store = &InstanceStoreMock::new(vec![Instance {
            name: "test".to_string(),
            arch: Arch::AMD64,
            user: "cubic".to_string(),
            cpus: 1,
            mem: 1024,
            disk_capacity: 1048576,
            ssh_port: 9000,
            hostfwd: Vec::new(),
        }]);

        InstanceShowCommand {
            instance: InstanceName::from_str("test").unwrap(),
        }
        .run(console, instance_store)
        .unwrap();

        assert_eq!(
            console.get_output(),
            "\
Arch:     amd64
CPUs:     1
Memory:   1.0 KiB
Disk:     1.0 MiB
User:     cubic
SSH Port: 9000
"
        );
    }

    #[test]
    fn test_show_command_failed() {
        let console = &mut ConsoleMock::new();
        let instance_store = &InstanceStoreMock::new(Vec::new());

        assert!(matches!(
            InstanceShowCommand {
                instance: InstanceName::from_str("test").unwrap()
            }
            .run(console, instance_store),
            Result::Err(Error::UnknownInstance(_))
        ));
    }
}